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’
};
_this.componentDidMount = function () {<br /> // 没参数<br /> console.log('componentDidMount', arguments)<br /> }
_this.shouldComponentUpdate = function () {<br /> // 接受两个参数nextProps,nextState<br /> console.log('shouldComponentUpdate', arguments)<br /> return true;<br /> }
_this.getSnapshotBeforeUpdate = function () {<br /> // 接受2个参数,return的值作为componentDidUpdate的第三个参数<br /> console.log('getSnapShotBeforeUpdate', arguments)<br /> return { a: 1 };<br /> }
_this.componentDidUpdate = function () {<br /> // 接受3个参数,prevProps,prevState<br /> console.log('componentDidUpdate', arguments)<br /> }
_this.componentDidCatch = function (error, info) {<br /> // 当自身发生异常时,是不会执行此声明周期的<br /> console.log('Son2 componentDidCatch', arguments)<br /> }
_this.componentWillUnmount = function () {<br /> // 卸载的声明周期<br /> }
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 () {
const self = this;<br /> console.log(self.state.name);
if (self.state.name !== 'CC') {<br /> a.a = 1;<br /> }
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);
- 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(current1, 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)
function mountIncompleteClassComponent(_current, workInProgress, Component, nextProps, renderExpirationTime) {
...
constructClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
// class组件的初始化生命周期在这个里面,getDerivedStateFromProps,componentWillMount
mountClassInstance(workInProgress, ctor, newProps, renderExpirationTime) ;
...
}
function updateClassComponent(current$$1, workInProgress, Component, nextProps, renderExpirationTime) {
// class组件的初始化生命周期在这个里面,getDerivedStateFromProps,componentWillMount
mountClassInstance(workInProgress, ctor, newProps, renderExpirationTime);
}
function mountIndeterminateComponent(_current, workInProgress, Component, renderExpirationTime) {
class组件的初始化生命周期在这个里面,getDerivedStateFromProps,componentWillMount
// mountClassInstance(workInProgress, ctor, newProps, renderExpirationTime)
}
function resumeMountClassInstance(workInProgress, ctor, newProps, renderExpirationTime) {
shouldComponentUpdate
var shouldUpdate = checkHasForceUpdateAfterProcessing() || checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext);
}
function updateClassInstance(current, workInProgress, ctor, newProps, renderExpirationTime) {
...
applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, newProps);
...
instance.componentWillUpdate(newProps, newState, nextContext);
}
beginWork$1, mountLazyComponent 里面被执行
function updateClassComponent(current$$1, workInProgress, Component, nextProps, renderExpirationTime) {
if (...) {
constructClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
// class组件的初始化生命周期在这个里面,getDerivedStateFromProps,componentWillMount
mountClassInstance(workInProgress, ctor, newProps, renderExpirationTime) ;
} else if (...) {
shouldUpdate = resumeMountClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
} else {
updateClassInstance()
}
}
updateClassComponent, resumeMountClassInstance 里面被执行。
shouldComponentUpdate 生命周期
function checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext) {
var shouldUpdate = instance.shouldComponentUpdate(newProps, newState, nextContext);
return shouldUpdate;
}
componentDidMount, componentDidMount 生命周期
function commitLifeCycles(finishedRoot, current$$1, finishedWork, committedExpirationTime) {
if (...) {
instance.componentDidMount();
} else {
instance.componentDidUpdate(prevProps, prevState, instance.__reactInternalSnapshotBeforeUpdate);
}
}
function commitLayoutEffects(root, committedExpirationTime) {
commitLifeCycles(root, current$$1, nextEffect, committedExpirationTime);
}
function commitRootImpl(root, renderPriorityLevel) {
...
invokeGuardedCallback(null, commitLayoutEffects, null, root, expirationTime);
...
}
getSnapshotBeforeUpdate 生命周期
function commitBeforeMutationLifeCycles(current$$1, finishedWork) {
...
var snapshot = instance.getSnapshotBeforeUpdate(finishedWork.elementType === finishedWork.type ? prevProps : resolveDefaultProps(finishedWork.type, prevProps), prevState);
instance.__reactInternalSnapshotBeforeUpdate = snapshot;
...
}
function commitBeforeMutationEffects() {
commitBeforeMutationLifeCycles(current$$1, nextEffect);
}
function commitRootImpl(root, renderPriorityLevel) {
...
invokeGuardedCallback(null, commitBeforeMutationEffects, null);
...
}
finalizeInitialChildren
setInitialProperties
Render
https://github.com/a8397550/react-source-share/blob/master/default/react-dom-default.js
Render 初始化,变更 17177 行
finishClassComponent(current$$1, workInProgress, Component, shouldUpdate, hasContext, renderExpirationTime) {
...
setCurrentPhase('render');
nextChildren = instance.render();
if (debugRenderPhaseSideEffects || debugRenderPhaseSideEffectsForStrictMode && workInProgress.mode & StrictMode) {
instance.render();
}
setCurrentPhase(null);
...
// Diff
reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
...
}
Diff reconcileChildren 16823 行
function reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime) {
if (current$$1 === null) {
// If this is a fresh new component that hasn't been rendered yet, we
// won't update its child set by applying minimal side-effects. Instead,
// we will add them all to the child before it gets rendered. That means
// we can optimize this reconciliation pass by not tracking side-effects.
workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderExpirationTime);
} else {
// If the current child is the same as the work in progress, it means that
// we haven't yet started any work on these children. Therefore, we use
// the clone algorithm to create a copy of all the current children.
// If we had any progressed work already, that is invalid at this point so
// let's throw it out.
workInProgress.child = reconcileChildFibers(workInProgress, current$$1.child, nextChildren, renderExpirationTime);
}
}
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,以寻求答案