1. 程式人生 > 其它 >react原始碼解析5.jsx&核心api

react原始碼解析5.jsx&核心api

react原始碼解析5.jsx&核心api

視訊講解(高效學習):點選學習

課程目錄:

1.開篇介紹和麵試題

2.react的設計理念

3.react原始碼架構

4.原始碼目錄結構和除錯

5.jsx&核心api

6.legacy和concurrent模式入口函式

7.Fiber架構

8.render階段

9.diff演算法

10.commit階段

11.生命週期

12.狀態更新流程

13.hooks原始碼

14.手寫hooks

15.scheduler&Lane

16.concurrent模式

17.context

18事件系統

19.手寫迷你版react

20.總結&第一章的面試題解答

21.demo

virtual Dom是什麼

一句話概括就是,用js物件表示dom資訊和結構,更新時重新渲染更新後的物件對應的dom,這個物件就是React.createElement()的返回結果

virtual Dom是一種程式設計方式,它以物件的形式儲存在記憶體中,它描述了我們dom的必要資訊,並且用類似react-dom等模組與真實dom同步,這一過程也叫協調(reconciler),這種方式可以宣告式的渲染相應的ui狀態,讓我們從dom操作中解放出來,在react中是以fiber樹的形式存放元件樹的相關資訊,在更新時可以增量渲染相關dom,所以fiber也是virtual Dom實現的一部分

為什麼要用virtual Dom

大量的dom操作慢,很小的更新都有可能引起頁面的重新排列,js物件優於在記憶體中,處理起來更快,可以通過diff演算法比較新老virtual Dom的差異,並且批量、非同步、最小化的執行dom的變更,以提高效能

另外就是可以跨平臺,jsx --> ReactElement物件 --> 真實節點,有中間層的存在,就可以在操作真實節點之前進行對應的處理,處理的結果反映到真實節點上,這個真實節點可以是瀏覽器環境,也可以是Native環境

virtual Dom真的快嗎?其實virtual Dom只是在更新的時候快,在應用初始的時候不一定快

const div = document.createElement('div');
let str = ''
for(let k in div){
  str+=','+k
}
console.log(str)

jsx&createElement

jsx可以宣告式的描述檢視,提升開發效率,通過babel可以轉換成React.createElement()的語法糖,也是js語法的擴充套件。

jsx是ClassComponent的render函式或者FunctionComponent的返回值,可以用來表示元件的內容,在經過babel編譯之後,最後會被編譯成React.createElement,這就是為什麼jsx檔案要宣告import React from 'react'的原因(react17之後不用匯入),你可以在 babel編譯jsx 站點檢視jsx被編譯後的結果

React.createElement的原始碼中做了如下幾件事

  • 處理config,把除了保留屬性外的其他config賦值給props
  • 把children處理後賦值給props.children
  • 處理defaultProps
  • 呼叫ReactElement返回一個jsx物件(virtual-dom)
//ReactElement.js
export function createElement(type, config, children) {
  let propName;

  const props = {};

  let key = null;
  let ref = null;
  let self = null;
  let source = null;

  if (config != null) {
    //處理config,把除了保留屬性外的其他config賦值給props
    //...
  }

  const childrenLength = arguments.length - 2;
  //把children處理後賦值給props.children
  //...

  //處理defaultProps
  //...

  return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );
}

const ReactElement = function(type, key, ref, self, source, owner, props) {
  const element = {
    $$typeof: REACT_ELEMENT_TYPE,//表示是ReactElement型別

    type: type,//class或function
    key: key,//key
    ref: ref,//ref屬性
    props: props,//props
    _owner: owner,
  };

  return element;
};


$$typeof表示的是元件的型別,例如在原始碼中有一個檢查是否是合法Element的函式,就是根object.$$typeof === REACT_ELEMENT_TYPE來判斷的

//ReactElement.js
export function isValidElement(object) {
  return (
    typeof object === 'object' &&
    object !== null &&
    object.$$typeof === REACT_ELEMENT_TYPE
  );
}

如果元件是ClassComponent則type是class本身,如果元件是FunctionComponent建立的,則type是這個function,原始碼中用ClassComponent.prototype.isReactComponent來區別二者。注意class或者function建立的元件一定要首字母大寫,不然後被當成普通節點,type就是字串。

jsx物件上沒有優先順序、狀態、effectTag等標記,這些標記在Fiber物件上,在mount時Fiber根據jsx物件來構建,在update時根據最新狀態的jsx和current Fiber對比,形成新的workInProgress Fiber,最後workInProgress Fiber切換成current Fiber。

render

//ReactDOMLegacy.js
export function render(
  element: React$Element<any>,//jsx物件
  container: Container,//掛載dom
  callback: ?Function,//回撥
) {
  
  return legacyRenderSubtreeIntoContainer(
    null,
    element,
    container,
    false,
    callback,
  );
}

可以看到render所做的事也就是呼叫legacyRenderSubtreeIntoContainer,這個函式在下一章講解,這裡重點關注ReactDom.render()使用時候的三個引數。

component

//ReactBaseClasses.js
function Component(props, context, updater) {
  this.props = props;//props屬性
  this.context = context;//當前的context
  this.refs = emptyObject;//ref掛載的物件
  this.updater = updater || ReactNoopUpdateQueue;//更新的對像
}

Component.prototype.isReactComponent = {};//表示是classComponent

component函式中主要在當前例項上掛載了props、context、refs、updater等,所以在元件的例項上能拿到這些,而更新主要的承載結構就是updater, 主要關注isReactComponent,它用來表示這個元件是類元件

總結:jsx是React.createElement的語法糖,jsx通過babel轉化成React.createElement函式,React.createElement執行之後返回jsx物件,也叫virtual-dom,Fiber會根據jsx物件和current Fiber進行對比形成workInProgress Fiber

pureComponent也很簡單,和component差不多,他會進行原型繼承,然後賦值isPureReactComponent

function PureComponent(props, context, updater) {
  this.props = props;
  this.context = context;
  this.refs = emptyObject;
  this.updater = updater || ReactNoopUpdateQueue;
}

const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy());
pureComponentPrototype.constructor = PureComponent;
Object.assign(pureComponentPrototype, Component.prototype);
pureComponentPrototype.isPureReactComponent = true;

export {Component, PureComponent};