shouldComponentUpdate componentWillUpdate
function shouldUpdate(classInstance, nextProps, nextState) {
let willUpdate = true;//表示组件是否要更新
//如果有shouldComponentUpdate方法并且shouldComponentUpdate方法返回了false
if (classInstance.shouldComponentUpdate && !classInstance.shouldComponentUpdate(nextProps, nextState)) {
willUpdate = false;//表示不需要更校招
}
//如果要更新,并且有componentWillUpdate方法,就执行它
if (willUpdate && classInstance.componentWillUpdate) {
classInstance.componentWillUpdate();
}
//不管要不要更新组件,状态都要更新
if (nextProps) {
classInstance.props = nextProps;
}
classInstance.state = nextState;//先把新状态赋值给实例的state
if (willUpdate) {
classInstance.forceUpdate();//强制更新
}
}
上面代码中if (nextProps) 这个分支,用于父组件更新时,子组件属性值和上次不一样了,也需要更新的场合,nextProps是新增的一个参数
nextProps参数的源流:
当执行setState或修改props的时候,都会触发emitUpdate,emitUpdate会接收新的属性,这个值就是nextProps,这个逻辑后期会加上
emitUpdate中会将nextProps挂到this上,在执行updateComponent -> shouldUpdate的时候,再从nextProps中取出使用
class Updater {
...
emitUpdate(nextProps) {
this.nextProps = nextProps;
//有可能是批量异步更新,也有可能是同步更新
if (updateQueue.isBatchingUpdate) {//批量异步更新
updateQueue.updaters.push(this);//不刷 新组件视图了,只是把自己这个updater实例添加到updateQueue等待生效
} else {//同步直接更新
this.updateComponent();
}
}
updateComponent() {
const { classInstance, nextProps, pendingStates } = this;
//如果属性变了或者状态变了都 会进入更新逻辑
if (nextProps || pendingStates.length > 0) {
shouldUpdate(classInstance, this.nextProps, this.getState());
}
}
componentDidUpdate
class Component {
...
forceUpdate() {
let oldRenderVdom = this.oldRenderVdom;//上一次类组件render方法计算得到的虚拟DOM
//let oldDOM = oldRenderVdom.dom;
let oldDOM = findDOM(oldRenderVdom);//获取 oldRenderVdom对应的真实DOM
//然后基于新的属性和状态,计算新的虚拟DOM
let newRenderVdom = this.render();
compareTwoVdom(oldDOM.parentNode, oldRenderVdom, newRenderVdom);
this.oldRenderVdom = newRenderVdom;
if (this.componentDidUpdate) {
this.componentDidUpdate(this.props, this.state);
}
}
componentWillMount componentDidMount
function mountClassComponent(vdom) {
let { type: ClassComponent, props, ref } = vdom;
let classInstance = new ClassComponent(props);
//如果类组件的虚拟DOM有ref属性,那么就把类的实例赋给ref.current属性
if (ref) ref.current = classInstance;
if (classInstance.componentWillMount) {//组件将要挂载
classInstance.componentWillMount();
}
let renderVdom = classInstance.render();
classInstance.oldRenderVdom = vdom.oldRenderVdom = renderVdom;
//把类组件的实例的render方法返回的虚拟DOM转成真实DOM
let dom = createDOM(renderVdom);
if (classInstance.componentDidMount) {//组件已经挂载
dom._componentDidMount = classInstance.componentDidMount.bind(classInstance);
}
return dom;
}
从上面代码中可以看到,对于componentDidMount这个钩子,我们先把它挂到了dom对象上,供后续使用
mountClassComponent的调用链为:
render -> mount -> createDOM -> mountClassComponent
在createDOM中,mountClassComponent的执行结果会直接返回到mount
在mount中继续执行createDOM调用完后的内容:
function mount(vdom, parentDOM) {
let newDOM = createDOM(vdom)
if (newDOM) {
parentDOM.appendChild(newDOM);
if (newDOM._componentDidMount) newDOM._componentDidMount();
}
}
在此处appendChild执行完后,才是挂载完成的时机,才应该执行执行componentDidMount这个钩子
这里没有把classInstance直接传给createDOM,再传到mount中,再获取到componentDidMount执行的原因是为了考虑后面函数组件中实现hook时与这里的mount共用