React 更新组件的源码解读
SunShinewyf opened this issue · 0 comments
SunShinewyf commented
React 在触发更新的时候执行的主要源码
精简主要代码如下:
updateComponent: function(
transaction,
prevParentElement,
nextParentElement,
prevUnmaskedContext,
nextUnmaskedContext
) {
var inst = this._instance;
var willReceive = false;
var nextContext;
//省略
var prevProps = prevParentElement.props;
var nextProps = nextParentElement.props;
//如果是 props 发生更新,而不是 state 的简单更新
if (prevParentElement !== nextParentElement) {
willReceive = true;
}
//如果是 props 更新,则触发 componentWillReceiveProps()
if (willReceive && inst.componentWillReceiveProps) {
if (__DEV__) {
measureLifeCyclePerf(
() => inst.componentWillReceiveProps(nextProps, nextContext),
this._debugID,
'componentWillReceiveProps',
);
} else {
inst.componentWillReceiveProps(nextProps, nextContext);
}
}
var nextState = this._processPendingState(nextProps, nextContext);
var shouldUpdate = true;
if (!this._pendingForceUpdate) {
if (inst.shouldComponentUpdate) {
//如果组件实例有 shouldComponentUpdate 则调用
shouldUpdate = inst.shouldComponentUpdate(nextProps, nextState, nextContext);
} else {
//如果使用了 PureComponent
if (this._compositeType === CompositeTypes.PureClass) {
shouldUpdate =
!shallowEqual(prevProps, nextProps) ||
!shallowEqual(inst.state, nextState);
}
}
}
this._updateBatchNumber = null;
if (shouldUpdate) {
//执行更新操作
this._performComponentUpdate(
nextParentElement,
nextProps,
nextState,
nextContext,
transaction,
nextUnmaskedContext
);
} else {
this._currentElement = nextParentElement;
this._context = nextUnmaskedContext;
inst.props = nextProps;
inst.state = nextState;
inst.context = nextContext;
}
},
在这个函数里,主要是判断是 state 还是 props 引起的变化,如果是 props,则执行实例的 componentWillReceiveProps() ,然后再判断实例是否提供了 shouldComponentUpdate 方法,如果提供,则直接调用,否则再判断是否使用了 PureComponent, 如果使用了,则使用浅比较来判断是否更新。 当前面两者都不满足,则执行 performComponentUpdate。
继续追踪代码,会发现 performComponentUpdate 里面调用了 _updateRenderdComponent ,源码如下:
_performComponentUpdate: function(
nextElement,
nextProps,
nextState,
nextContext,
transaction,
unmaskedContext
) {
var inst = this._instance;
var hasComponentDidUpdate = Boolean(inst.componentDidUpdate);
var prevProps;
var prevState;
var prevContext;
if (hasComponentDidUpdate) {
prevProps = inst.props;
prevState = inst.state;
prevContext = inst.context;
}
if (inst.componentWillUpdate) {
inst.componentWillUpdate(nextProps, nextState, nextContext); }
}
this._currentElement = nextElement;
this._context = unmaskedContext;
inst.props = nextProps;
inst.state = nextState;
inst.context = nextContext;
if (inst.unstable_handleError) {
this._updateRenderedComponentWithErrorHandling(transaction, unmaskedContext);
} else {
this._updateRenderedComponent(transaction, unmaskedContext);
}
if (hasComponentDidUpdate) {
if (__DEV__) {
transaction.getReactMountReady().enqueue(() => {
measureLifeCyclePerf(
inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext),
this._debugID,
'componentDidUpdate'
);
});
} else {
transaction.getReactMountReady().enqueue(
inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext),
inst
);
}
}
},
_updateRenderedComponent 里又调用了 _updateRenderedComponentWithNextElement ,_updateRenderedComponentWithNextElement 里面 又通过 shouldUpdateReactComponent
来判断组件是否需要更新:
_updateRenderedComponentWithNextElement: function(
transaction,
context,
nextRenderedElement,
safely
) {
var prevComponentInstance = this._renderedComponent;
var prevRenderedElement = prevComponentInstance._currentElement;
if (shouldUpdateReactComponent(prevRenderedElement, nextRenderedElement)) {
ReactReconciler.receiveComponent(
prevComponentInstance,
nextRenderedElement,
transaction,
this._processChildContext(context)
);
} else {
var oldHostNode = ReactReconciler.getHostNode(prevComponentInstance);
ReactReconciler.unmountComponent(
prevComponentInstance,
safely,
false /* skipLifecycle */
);
var nodeType = ReactNodeTypes.getType(nextRenderedElement);
this._renderedNodeType = nodeType;
var child = this._instantiateReactComponent(
nextRenderedElement,
nodeType !== ReactNodeTypes.EMPTY /* shouldHaveDebugID */
);
this._renderedComponent = child;
var nextMarkup = ReactReconciler.mountComponent(
child,
transaction,
this._hostParent,
this._hostContainerInfo,
this._processChildContext(context),
debugID
);
this._replaceNodeWithMarkup(
oldHostNode,
nextMarkup,
prevComponentInstance
);
}
},
shouldUpdateReactComponent 的源码
如下:
function shouldUpdateReactComponent(prevElement, nextElement) {
var prevEmpty = prevElement === null || prevElement === false;
var nextEmpty = nextElement === null || nextElement === false;
if (prevEmpty || nextEmpty) {
return prevEmpty === nextEmpty;
}
var prevType = typeof prevElement;
var nextType = typeof nextElement;
if (prevType === 'string' || prevType === 'number') {
return (nextType === 'string' || nextType === 'number');
} else {
return (
nextType === 'object' &&
prevElement.type === nextElement.type &&
prevElement.key === nextElement.key
);
}
}
由此发现 React.Component 在这里使用了深比较, 然后之后的更新流程就是使用 diff 算法来比较两次的 Virtual DOM 是否改变从而执行更新操作。