天天看點

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隻是在更新的時候快,在應用初始的時候不一定快

react源碼解析5.jsx&核心api

react源碼5.1

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

複制

react源碼解析5.jsx&核心api

react源碼5.2

jsx&createElement

jsx可以聲明式的描述視圖,提升開發效率,通過babel可以轉換成React.createElement()的文法糖,也是js文法的擴充。

jsx是ClassComponent的render函數或者FunctionComponent的傳回值,可以用來表示元件的内容,在經過babel編譯之後,最後會被編譯成

React.createElement

,這就是為什麼jsx檔案要聲明

import React from 'react'

的原因(react17之後不用導入),你可以在 <a name="https://www.babeljs.cn/repl">babel編譯jsx</a> 站點檢視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};           

複制