React 16.9.0

React与React-DOM的关系

首先我先介绍React与React-DOM他们之间的关系。

react.js 中定义了React中的各种对象

var hasSymbol = typeof Symbol === ‘function’ && Symbol.for;
var REACT_ELEMENT_TYPE = hasSymbol ? Symbol.for(‘react.element’) : 0xeac7;
var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol.for(‘react.fragment’) : 0xeacb;
var REACT_PROFILER_TYPE = hasSymbol ? Symbol.for(‘react.profiler’) : 0xead2;
var REACT_CONTEXT_TYPE = hasSymbol ? Symbol.for(‘react.context’) : 0xeace;
var REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for(‘react.forward_ref’) : 0xead0;
// … 等等
React介绍React.createElement, React.Component, React.PureComponent …

var type = “div”;
var props = {}
var children = “777”

// 字符类型的组件
var strReactElement = React.createElement(type, props, children);

// Class组件 React.Component类型或React.PureComponent类型的组件
// reactClassElement 请参考下面的定义
var classReactElement = React.createElement(reactClassElement, props, children);

// 函数组件
var fnReactElement = React.createElement(function(){
var state = React.useState(“fnReactElement”);
return React.createElement(‘div’, {
onClick: function(){
state1
}
}, state[0]);
});

var reactClassElement = (function (Component) {
/*
Component.prototype如下

isMounted: (…)
replaceState: (…)
isReactComponent: {}
setState: ƒ (partialState, callback)
forceUpdate: ƒ (callback)
constructor: ƒ Component(props, context, updater)
get isMounted: ƒ ()
get replaceState: ƒ ()

*/

Mod.prototype = Object.create(Component.prototype);
Mod.prototype.constructor = Mod;
Mod.proto = Component;

function Mod(props) {
var _this;
_this = Component.call(this, props) || this;
_this.state = {
name: ‘CC’
};

  1. _this.componentDidMount = function () {<br /> // 没参数<br /> console.log('componentDidMount', arguments)<br /> }
  2. _this.shouldComponentUpdate = function () {<br /> // 接受两个参数nextProps,nextState<br /> console.log('shouldComponentUpdate', arguments)<br /> return true;<br /> }
  3. _this.getSnapshotBeforeUpdate = function () {<br /> // 接受2个参数,return的值作为componentDidUpdate的第三个参数<br /> console.log('getSnapShotBeforeUpdate', arguments)<br /> return { a: 1 };<br /> }
  4. _this.componentDidUpdate = function () {<br /> // 接受3个参数,prevProps,prevState<br /> console.log('componentDidUpdate', arguments)<br /> }
  5. _this.componentDidCatch = function (error, info) {<br /> // 当自身发生异常时,是不会执行此声明周期的<br /> console.log('Son2 componentDidCatch', arguments)<br /> }
  6. _this.componentWillUnmount = function () {<br /> // 卸载的声明周期<br /> }
  7. return _this;<br /> }<br /> <br /> // class 组件才具有声明周期,必须要继承React.Component或者React.PureComponent组件<br /> // 这个是一个静态的方法,在ES6 的class语法中使用 static 声明 <br /> Mod.getDerivedStateFromProps = function () {<br /> return null;<br /> }

Mod.prototype.render = function () {

  1. const self = this;<br /> console.log(self.state.name);
  2. if (self.state.name !== 'CC') {<br /> a.a = 1;<br /> }
  3. return React.createElement(<br /> 'div',<br /> {<br /> children: "CC",<br /> key: 'Parent',<br /> id: 'Parent',<br /> onClick: () => {<br /> self.setState({<br /> name: 'Parent',<br /> })<br /> }<br /> }<br /> );<br /> }<br /> return Mod;<br />}(React.Component))<br />react-dom.js 在执行时,首先会检查有没有React对象存在,没有的话会直接抛错

// ReactDOM.render可以接受3个参数
// 参数1 必须是一个ReactElement元素
// 参数2 必须是一个document或DOMElement元素
// 参数3 callback函数
ReactDOM.render(Div, document.getElementById(‘root’));
请注意只要class组件才能使用生命周期。函数组件可以使用hooks

React 负责创建ReactElement元素,和提供一些与其相关的特意属性和方法,这个会放在后期的内容中介绍

ReactDOM负责将树(Tree)结构的ReactElement渲染成真实DOM,以及给真实DOM中加上属性与方法,并且提供了处理变更DOM方法,虚拟DOM树(或者说是链放在后面给大家讲解,先让大家有一个概念)

今天先介绍,真实DOM是如何被渲染出来的呢? 以及如果要想了解这个问题,应该如何阅读ReactDOM的代码呢,如何在源码中找到这个地方,这里先介绍一下源码阅读的注意事项,以及一些顺序。

ReactDOM 初始化渲染流程分析

在ReactDOM中可以先了解FiberRootNode预计FiberNode两个类型。

// 以下提供了阅读的逆序的方法,请注意执行顺序是倒着来的哦,后面的方法的执行将执行前面的方法,
先从真实DOM从哪里出现开始,以下代码都在react-dom.js中
1.createElement 负责创建真实DOM哦

2.createInstance 验证type并在真实DOM中加入FiberNode,Props,
真实DOM中其实有对应的属性与其对应
var randomKey = Math.random().toString(36).slice(2);
var internalInstanceKey = ‘reactInternalInstance$’ + randomKey;
var internalEventHandlersKey = ‘
reactEventHandlers$’ + randomKey;
node[internalInstanceKey] = FiberNode;
node[internalEventHandlersKey] = Props;

3.completeWork [kəmˈpliːt 完全的] 此方法负责将Props里的属性和事件渲染到真实DOM中
finalizeInitialChildren [[ˈfaɪnəlaɪz] 把(计划、旅行、项目等)最后定下来; 定案; ] [ɪˈnɪʃl]
setInitialProperties 初始化属性
setInitialDOMProperties 此方法对props进行了处理 会对所有的定义的React事件进行处理

4.completeUnitOfWork 处理FiberNode链等等内容..

5.performUnitOfWork 处理FiberNode链等等内容..
创建class组件,初始化生命周期等

6.workLoopSync while循环去处理一个链式结构,他还有一个方法是workLoop这个是异步的做同样的事情

7.renderRoot 初始化时只执行一次,此方法会返回一个callback callback=commitRoot.bind(…)
commitRoot这个方法也很重要哦
renderRoot函数还负责workLoopSync这个的执行

8.scheduleWork 初始化时只执行一次,scheduleUpdateOnFiber方法别名scheduleWork,下面有一段逻辑
var callback = renderRoot(root, Sync, true); // 返回了一个这个哦 commitRoot.bind(…)
while (callback !== null) {
callback = callback(true);
}
commitRoot 初始化时,也只被执行了一次

9.scheduleRootUpdate 负责创建update对象
var update = createUpdate(expirationTime, suspenseConfig);
此对象会放在后面重点讲解

10.updateContainerAtExpirationTime 处理容器相关的内容
并 return scheduleRootUpdate(
current$$1, element, expirationTime, suspenseConfig, callback);

  1. updateContainer

12.legacyRenderSubtreeIntoContainer 负责创建FiberRootNode与FiberNode
可以说他是用来创建链表的头的

13.render 检查容器符不符合要求
container.nodeType 是不是一个元素或者document对象
阅读源码时,可以使用http://window.xxx.xxx记录函数被执行的次数,用以了解触发一个动作时,执行函数的增量次数

window._react_state.workLoopSyncFNCount =
(window._react_state.workLoopSyncFNCount || 0) + 1;
以上介绍的内容,会在后续章节中继续精细的拆解,会介绍React虚拟DOM长成什么样子,今天已经介绍了真实DOM如何生成以及属性和方法是如何加入到真实DOM中的,为大家的react源码阅读带来更轻松的体验。

组件树的渲染规则
按顺序找到叶子节点,如果在找叶子节点的过程中碰到了text节点,就先渲染出来,找到叶子节点,从下往上渲染dom元素

beginWork$1(current1, workInProgress, renderExpirationTime)
mountLazyComponent(_current, workInProgress, elementType, updateExpirationTime, renderExpirationTime)
updateClassComponent(current
1, workInProgress, Component, nextProps, renderExpirationTime)
1.constructClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
2.mountClassInstance(workInProgress, ctor, newProps, renderExpirationTime) class组件的初始化生命周期在这个里面,getDerivedStateFromProps,componentWillMount
初始化Class组件
performUnitOfWork
beginWork1 = function (current1, unitOfWork, expirationTime)
beginWork$1(current$$1, workInProgress, renderExpirationTime)

  1. function mountIncompleteClassComponent(_current, workInProgress, Component, nextProps, renderExpirationTime) {
  2. ...
  3. constructClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
  4. // class组件的初始化生命周期在这个里面,getDerivedStateFromProps,componentWillMount
  5. mountClassInstance(workInProgress, ctor, newProps, renderExpirationTime) ;
  6. ...
  7. }
  8. function updateClassComponent(current$$1, workInProgress, Component, nextProps, renderExpirationTime) {
  9. // class组件的初始化生命周期在这个里面,getDerivedStateFromProps,componentWillMount
  10. mountClassInstance(workInProgress, ctor, newProps, renderExpirationTime);
  11. }
  12. function mountIndeterminateComponent(_current, workInProgress, Component, renderExpirationTime) {
  13. class组件的初始化生命周期在这个里面,getDerivedStateFromPropscomponentWillMount
  14. // mountClassInstance(workInProgress, ctor, newProps, renderExpirationTime)
  15. }
  1. function resumeMountClassInstance(workInProgress, ctor, newProps, renderExpirationTime) {
  2. shouldComponentUpdate
  3. var shouldUpdate = checkHasForceUpdateAfterProcessing() || checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext);
  4. }
  5. function updateClassInstance(current, workInProgress, ctor, newProps, renderExpirationTime) {
  6. ...
  7. applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, newProps);
  8. ...
  9. instance.componentWillUpdate(newProps, newState, nextContext);
  10. }
  11. beginWork$1, mountLazyComponent 里面被执行
  12. function updateClassComponent(current$$1, workInProgress, Component, nextProps, renderExpirationTime) {
  13. if (...) {
  14. constructClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
  15. // class组件的初始化生命周期在这个里面,getDerivedStateFromProps,componentWillMount
  16. mountClassInstance(workInProgress, ctor, newProps, renderExpirationTime) ;
  17. } else if (...) {
  18. shouldUpdate = resumeMountClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
  19. } else {
  20. updateClassInstance()
  21. }
  22. }
  1. updateClassComponent, resumeMountClassInstance 里面被执行。
  2. shouldComponentUpdate 生命周期
  3. function checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext) {
  4. var shouldUpdate = instance.shouldComponentUpdate(newProps, newState, nextContext);
  5. return shouldUpdate;
  6. }
  1. componentDidMount, componentDidMount 生命周期
  2. function commitLifeCycles(finishedRoot, current$$1, finishedWork, committedExpirationTime) {
  3. if (...) {
  4. instance.componentDidMount();
  5. } else {
  6. instance.componentDidUpdate(prevProps, prevState, instance.__reactInternalSnapshotBeforeUpdate);
  7. }
  8. }
  9. function commitLayoutEffects(root, committedExpirationTime) {
  10. commitLifeCycles(root, current$$1, nextEffect, committedExpirationTime);
  11. }
  12. function commitRootImpl(root, renderPriorityLevel) {
  13. ...
  14. invokeGuardedCallback(null, commitLayoutEffects, null, root, expirationTime);
  15. ...
  16. }
  1. getSnapshotBeforeUpdate 生命周期
  2. function commitBeforeMutationLifeCycles(current$$1, finishedWork) {
  3. ...
  4. var snapshot = instance.getSnapshotBeforeUpdate(finishedWork.elementType === finishedWork.type ? prevProps : resolveDefaultProps(finishedWork.type, prevProps), prevState);
  5. instance.__reactInternalSnapshotBeforeUpdate = snapshot;
  6. ...
  7. }
  8. function commitBeforeMutationEffects() {
  9. commitBeforeMutationLifeCycles(current$$1, nextEffect);
  10. }
  11. function commitRootImpl(root, renderPriorityLevel) {
  12. ...
  13. invokeGuardedCallback(null, commitBeforeMutationEffects, null);
  14. ...
  15. }

finalizeInitialChildren
setInitialProperties

Render

https://github.com/a8397550/react-source-share/blob/master/default/react-dom-default.js

Render 初始化,变更 17177 行

  1. finishClassComponent(current$$1, workInProgress, Component, shouldUpdate, hasContext, renderExpirationTime) {
  2. ...
  3. setCurrentPhase('render');
  4. nextChildren = instance.render();
  5. if (debugRenderPhaseSideEffects || debugRenderPhaseSideEffectsForStrictMode && workInProgress.mode & StrictMode) {
  6. instance.render();
  7. }
  8. setCurrentPhase(null);
  9. ...
  10. // Diff
  11. reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
  12. ...
  13. }

Diff reconcileChildren 16823 行

  1. function reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime) {
  2. if (current$$1 === null) {
  3. // If this is a fresh new component that hasn't been rendered yet, we
  4. // won't update its child set by applying minimal side-effects. Instead,
  5. // we will add them all to the child before it gets rendered. That means
  6. // we can optimize this reconciliation pass by not tracking side-effects.
  7. workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderExpirationTime);
  8. } else {
  9. // If the current child is the same as the work in progress, it means that
  10. // we haven't yet started any work on these children. Therefore, we use
  11. // the clone algorithm to create a copy of all the current children.
  12. // If we had any progressed work already, that is invalid at this point so
  13. // let's throw it out.
  14. workInProgress.child = reconcileChildFibers(workInProgress, current$$1.child, nextChildren, renderExpirationTime);
  15. }
  16. }

Diff reconcileChildFibers 14705 行

...
if (isObject) {
  switch (newChild.$$typeof) {
    case REACT_ELEMENT_TYPE:
      return placeSingleChild(reconcileSingleElement(returnFiber, currentFirstChild, newChild, expirationTime));
    case REACT_PORTAL_TYPE:
      return placeSingleChild(reconcileSinglePortal(returnFiber, currentFirstChild, newChild, expirationTime));
  }
}

if (typeof newChild === 'string' || typeof newChild === 'number') {
  return placeSingleChild(reconcileSingleTextNode(returnFiber, currentFirstChild, '' + newChild, expirationTime));
}

if (isArray(newChild)) {
  return reconcileChildrenArray(returnFiber, currentFirstChild, newChild, expirationTime);
}
...

Diff reconcileSingleElement 14634 行

...
// 会调用 createWorkInProgress 会创建一个新的FiberNode
useFiber(...)
...

疑问 React Diff 进行比较吗?

研究此方法,createWorkInProgress,以寻求答案