手写react6-生命周期

187 阅读2分钟

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共用