React源码分析(二)

202 阅读26分钟

React源码分析(一)

6.类组件老生命周期

老生命周期

6.1.基本生命周期实现

6.1.1.实例
import React from 'react';
import ReactDOM from 'react-dom';

class Counter extends React.Component {
  static defaultProps = {
    name: '计数器:'
  }

  constructor(props) {
    super(props)
    this.state = { num: 0 }
    console.log('Counter 1.constructor 属性,状态初始化')
  }

  componentWillMount(){
    console.log('Counter 2.componentWillMount 组件将要挂载')
  }

  componentDidMount(){
    console.log('Counter 4.componentDidMount 组件挂载完成')
  }

  handlerClick = () => {
    this.setState({num:this.state.num+1});
  }

  shouldComponentUpdate(nextProps,nextState){
    console.log('Counter 5.shouldComponentUpdate 询问组件是否需要更新?')
    return nextState.num%2===0;//num为2的倍数更是视图,状态一致会更新
  }

  componentWillUpdate(){
    console.log('Counter 6.componentWillUpdate 组件将要更新')
  }

  componentDidUpdate(){
    console.log('Counter 7.componentDidUpdate 组件更新完成')
  }

  render() {
    console.log('Counter 3.render 生成虚拟DOM')
    return (
      <div>
        <p>{this.props.name}:{this.state.num}</p>
        <button onClick={this.handlerClick}>加2</button>
      </div>
    )
  }
}
ReactDOM.render(<Counter name='张三' />, document.getElementById('root'));

日志:

  • 初始化日志

日志可以看出,初始化的时候,分别走了constructor->componentWillMount->render->componentDidMount

 Counter 1.constructor 属性,状态初始化
 Counter 2.componentWillMount 组件将要挂载
 Counter 3.render 生成虚拟DOM
 Counter 4.componentDidMount 组件挂载完成
  • 点击第一次日志

当第一次点击按钮时,走了shouldComponentUpdate方法,此时询问是否需要更新组件,此时num=1不是2的倍数,返回的是false,因此不更新组件

  Counter 1.constructor 属性,状态初始化
  Counter 2.componentWillMount 组件将要挂载
  Counter 3.render 生成虚拟DOM
  Counter 4.componentDidMount 组件挂载完成
+ Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  • 点击第二次日志:

第二次点击按钮时,继续询问是否需要更新组件,此时num=2是2的倍数,返回的是true,因此更新组件,分别需要走shouldComponentUpdate->componentWillUpdate->render->componentDidUpdate

  Counter 1.constructor 属性,状态初始化
  Counter 2.componentWillMount 组件将要挂载
  Counter 3.render 生成虚拟DOM
  Counter 4.componentDidMount 组件挂载完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
+ Counter 5.shouldComponentUpdate 询问组件是否需要更新?
+ Counter 6.componentWillUpdate 组件将要更新
+ Counter 3.render 生成虚拟DOM
+ Counter 7.componentDidUpdate 组件更新完成
6.1.2src/react-dom.js
 import {addEvent} from './event';

/**
 * 给跟容器挂载的时候
 * @param {*} vdom 需要渲染的虚拟DOM
 * @param {*} container 容器
 */
function render(vdom, container) {
    const dom = createDOM(vdom);
    //挂载真实DOM
    container.appendChild(dom);
+   //调用生命周期方法componentDidMount
+   dom.componentDidMount&&dom.componentDidMount();
}

/**
 * 创建证实DOM
 * @param {*} vdom 虚拟DOM
 */
export function createDOM(vdom) {
    if (typeof vdom === 'string' || typeof vdom === 'number') {//vdom是字符串或者数组
        return document.createTextNode(vdom);
    }
    const { 
        type, 
        props } = vdom;
    //创建真实DOM
    let dom;
    if (typeof type === 'function') {//自定义函数组件
        if(type.isReactComponent){//类组件
            return mountClassComponent(vdom);
        }else{//函数组件
            return mountFunctionComponent(vdom);
        }
    } else {//原生组件
        dom = document.createElement(type);
    }
    //使用虚拟DOM的属性更新刚创建出来的真实DOM的属性
    updateProps(dom, props);
    //儿子是一个文本
    if (typeof props.children === 'string' || typeof props.children === 'number') {
        dom.textContent = props.children
    } else if (typeof props.children === 'object' && props.children.type) {//只有一个儿子,并且是虚拟DOM
        render(props.children, dom);//把儿子挂载的自己身上
    } else if (Array.isArray(props.children)) {//有多个儿子
        reconcileChildren(props.children, dom);
    } else {
        document.textContent = props.children ? props.children.toString() : ''
    }
    return dom;
}

/**
 * 把一个类型为自定义类组件的虚拟DOM转化为一个真实DOM并返回
 * @param {*} vdom 类型为自定义类组件的虚拟DOM
 */
function mountClassComponent(vdom){
    const {type:Clazz,props}=vdom;
    //获取类的实例
    const classInstance=new Clazz(props);
+   //调用生命周期方法componentWillMount
+   if(classInstance.componentWillMount){
+       classInstance.componentWillMount();
+   }
    //获取虚拟DOM
    const renderVdom=classInstance.render();
    //获取真实DOM
    const dom=createDOM(renderVdom);
    
+   if(classInstance.componentDidMount){
+       dom.componentDidMount=classInstance.componentDidMount;
+   }
    //将真实dom挂到实例上上
    classInstance.dom=dom;
    return dom;
}

/**
 * 把一个类型为自定义函数组件的虚拟DOM转换为一个真实DOM并返回
 * @param {*} vdom 类型为自定义函数组件的虚拟DOM
 */
function mountFunctionComponent(vdom) {
    const { type: FunctionComponent, props } = vdom;
    const renderVdom = FunctionComponent(props);
    return createDOM(renderVdom);
}

/**
 * 
 * @param {*} childrenVdom 孩子门的虚拟DOM
 * @param {*} parentDOM 要挂载到的真实DOM
 */
function reconcileChildren(childrenVdom, parentDOM) {
    for (let i = 0; i < childrenVdom.length; i++) {
        const child = childrenVdom[i];
        render(child, parentDOM);//把儿子挂载的自己身上
    }
}
/**
 * 使用虚拟DOM的属性更新刚创建出来的真实DOM的属性
 * @param {*} dom 真实DOM
 * @param {*} props 虚拟DOM属性
 */
function updateProps(dom, props) {
    for (const key in props) {
        if (key === 'children') continue;//单独处理,不再此处处理
        if (key === 'style') {
            const styleObj = props.style;
            for (const attr in styleObj) {
                dom.style[attr] = styleObj[attr];
            }
        } else if(key.startsWith('on')){//onClick=>onclick
            // dom[key.toLocaleLowerCase()]=props[key];
            addEvent(dom,key.toLocaleLowerCase(),props[key]);
        }else {//在JS中定义class使用的是className,所以不要改
            dom[key] = props[key];
        }
    }
}

const ReactDOM = {
    render
}

export default ReactDOM;
6.1.3.src/Component.js
import { createDOM } from './react-dom'

//更新队列
export let updateQueue = {
    isBatchingUpdate: false,//当前是否处于批量更新模式
    updaters: new Set(),
    batchUpdate(){//批量更新
        for(let updater of this.updaters){
            updater.updateClassComponent();
        }
        this.isBatchingUpdate=false;
    }
}
//更新器
class Updater {
    constructor(classInstance) {
        this.classInstance = classInstance;//类组件的实例
        this.pendingStates = [];//等待生效的状态,可能是一个对象,也可能是一个函数
        this.cbs = [];//存放回调
    }

    /**
     * 
     * @param {*} partialState 等待更新生效的状态
     * @param {*} cb 状态更新的回调
     */
    addState(partialState, cb) {
        this.pendingStates.push(partialState);
        typeof cb === 'function' && this.cbs.push(cb);
+       this.emitUpdate();
    }

    //一个组件不管属性变了,还是状态变了,都会更新
+  emitUpdate(newProps){
+       if (updateQueue.isBatchingUpdate) {//当前处于批量更新模式,先缓存updater
+           updateQueue.updaters.add(this);//本次setState调用结束
+       } else {//当前处于非批量更新模式,执行更新
+           this.updateClassComponent();//直接更新组件
+       }
+   }

    updateClassComponent() {
        const { classInstance, pendingStates, cbs } = this;
        if (pendingStates.length > 0) {//有setState
+           shouldUpdate(classInstance,this.getState())
            // classInstance.state = this.getState();//计算新状态
            // classInstance.forceUpdate();
            // cbs.forEach(cb=>cb());
            // cbs.length=0;
        }
    }

    getState() {//计算新状态
        const { classInstance, pendingStates } = this;
        let { state } = classInstance;//获取老状态
        pendingStates.forEach(newState => {
            //newState可能是对象,也可能是函数,对象setState的两种方式
            if (typeof newState === 'function') {
                newState = newState(state);
            }
            state = { ...state, ...newState };
        })
        pendingStates.length = 0;//清空数组
        return state;
    }
}

/**
 * 判断组件是否需要更新
 * @param {*} classInstance 类组件实例
 * @param {*} newState 新状态
 */
+ function shouldUpdate(classInstance,newState){
+   //不管组件要不要更新,组件的state一定会改变
+   classInstance.state=newState;
+   //如果有这个方法,并且这个方法的返回值为false,则不需要继续向下更新了,否则就更新
+   if(classInstance.shouldComponentUpdate&&!classInstance.shouldComponentUpdate(classInstance.props,newState)){
+       return;
+   }
+   classInstance.forceUpdate()
+ }
class Component {
    //用来判断是类组件
    static isReactComponent = true;
    constructor(props) {
        this.props = props;
        this.state = {};
        //每个类组件都有一个更新器
        this.updater = new Updater(this);
    }

    setState(partialState, cb) {
        this.updater.addState(partialState, cb);
        // const { state } = this;
        // this.state = { ...state, ...partialState };
        // const newVdom = this.render();
        // mountClassComponent(this, newVdom);
    }

    forceUpdate() {
+       //执行生命周期方法componentWillUpdate
+       this.componentWillUpdate&&this.componentWillUpdate();
        const newVdom = this.render();
        updateClassComponent(this, newVdom);
    }
}

/**
 * 用新的真实dom替换老得真实DOM
 * @param {*} classInstance 类组件实例
 * @param {*} newVdom 新的虚拟DOM
 */
function updateClassComponent(classInstance, newVdom) {
    const newDOM = createDOM(newVdom);
    const oldDOM = classInstance.dom;
    oldDOM.parentNode.replaceChild(newDOM, oldDOM);
+   //调用生命周期方法componentDidUpdate
+   classInstance.componentDidUpdate&&classInstance.componentDidUpdate();
    classInstance.dom = newDOM;
}
export default Component;

6.2.子组件生命周期

6.2.1.实例
import React from 'react';
import ReactDOM from 'react-dom';

class Counter extends React.Component {
  static defaultProps = {
    name: '计数器:'
  }

  constructor(props) {
    super(props)
    this.state = { num: 0 }
    console.log('Counter 1.constructor 属性,状态初始化')
  }

  componentWillMount() {
    console.log('Counter 2.componentWillMount 组件将要挂载')
  }

  componentDidMount() {
    console.log('Counter 4.componentDidMount 组件挂载完成')
  }

  handlerClick = () => {
    this.setState({ num: this.state.num + 1 });
  }

  shouldComponentUpdate(nextProps, nextState) {
    console.log('Counter 5.shouldComponentUpdate 询问组件是否需要更新?')
    return nextState.num % 2 === 0;//num为2的倍数更是视图,状态一致会更新
  }

  componentWillUpdate() {
    console.log('Counter 6.componentWillUpdate 组件将要更新')
  }

  componentDidUpdate() {
    console.log('Counter 7.componentDidUpdate 组件更新完成')
  }

  render() {
    console.log('Counter 3.render 生成虚拟DOM')
    return (
      <div>
        <p>{this.props.name}:{this.state.num}</p>
        {this.state.num===4?null:<ChildCounter num={this.state.num}/>}
        <button onClick={this.handlerClick}>加2</button>
      </div>
    )
  }
}

class ChildCounter extends React.Component {
  constructor(props){
    super(props);
    console.log('ChildCounter 1.constructor 属性,状态初始化')
  }
  componentWillMount() {
    console.log('ChildCounter 2.componentWillMount 组件将要挂载')
  }

  componentDidMount() {
    console.log('ChildCounter 4.componentDidMount 组件挂载完成')
  }

  componentWillReceiveProps(newProps){//第一次不会执行,之后属性更新时会执行
    console.log('ChildCounter 5.componentWillReceiveProps 组件将要接收新的props')
  }

  shouldComponentUpdate(nextProps, nextState) {
    console.log('ChildCounter 6.shouldComponentUpdate 询问组件是否需要更新?')
    return nextProps.num % 3 === 0;//num为3的倍数更是视图,状态一致会更新
  }

  componentWillUpdate() {
    console.log('ChildCounter 7.componentWillUpdate 组件将要更新')
  }

  componentDidUpdate() {
    console.log('ChildCounter 8.componentDidUpdate 组件更新完成')
  }

  componentWillUnmount(){
    console.log('ChildCounter 9.componentWillUnmount 组件将被卸载')
  }

  render() {
    console.log('ChildCounter 3.render 生成虚拟DOM')
    return <div>{this.props.num}</div>
  }
}
ReactDOM.render(<Counter name='张三' />, document.getElementById('root'));

日志分心:

  • 初始化日志

初始化时分别会走:父组件constructor->父组件componentWillMount->父组件render->子组件constructor->子组件componentWillMount->子组件render->子组件componentDidMount->父组件componentDidMount

Counter 1.constructor 属性,状态初始化
Counter 2.componentWillMount 组件将要挂载
Counter 3.render 生成虚拟DOM
ChildCounter 1.constructor 属性,状态初始化
ChildCounter 2.componentWillMount 组件将要挂载
ChildCounter 3.render 生成虚拟DOM
ChildCounter 4.componentDidMount 组件挂载完成
Counter 4.componentDidMount 组件挂载完成
  • 第一次点击

第一次点击时,父组件走了shouldComponentUpdate询问是否需要更新组件,此时num=1,返回的false,父组件不更新,子组件也不会更新

  Counter 1.constructor 属性,状态初始化
  Counter 2.componentWillMount 组件将要挂载
  Counter 3.render 生成虚拟DOM
  ChildCounter 1.constructor 属性,状态初始化
  ChildCounter 2.componentWillMount 组件将要挂载
  ChildCounter 3.render 生成虚拟DOM
  ChildCounter 4.componentDidMount 组件挂载完成
  Counter 4.componentDidMount 组件挂载完成
+ Counter 5.shouldComponentUpdate 询问组件是否需要更新? 
  • 第二次点击

第二次点击时,父组件走shouldComponentUpdate,此时num=2是2的倍数,返回的是true,父组件需要更新,调用componentWillUpdate父组件将要更新,从新render,此时子组件的componentWillReceiveProps被调用,子组件将要接收新的props,接着子组件的shouldComponentUpdate被调用,询问子组件是否需要更新,此时num=2,不是3的倍数,返回的是false,子组件不更新,接着执行componentDidUpdate父组件更新完成

  Counter 1.constructor 属性,状态初始化
  Counter 2.componentWillMount 组件将要挂载
  Counter 3.render 生成虚拟DOM
  ChildCounter 1.constructor 属性,状态初始化
  ChildCounter 2.componentWillMount 组件将要挂载
  ChildCounter 3.render 生成虚拟DOM
  ChildCounter 4.componentDidMount 组件挂载完成
  Counter 4.componentDidMount 组件挂载完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
+ Counter 5.shouldComponentUpdate 询问组件是否需要更新?
+ Counter 6.componentWillUpdate 组件将要更新
+ Counter 3.render 生成虚拟DOM
+ ChildCounter 5.componentWillReceiveProps 组件将要接收新的props
+ ChildCounter 6.shouldComponentUpdate 询问组件是否需要更新?
+ Counter 7.componentDidUpdate 组件更新完成
  • 第三次点击

第三次点击时,父组件走了shouldComponentUpdate询问是否需要更新组件,此时num=3,返回的false,父组件不更新,子组件也不会更新

  Counter 1.constructor 属性,状态初始化
  Counter 2.componentWillMount 组件将要挂载
  Counter 3.render 生成虚拟DOM
  ChildCounter 1.constructor 属性,状态初始化
  ChildCounter 2.componentWillMount 组件将要挂载
  ChildCounter 3.render 生成虚拟DOM
  ChildCounter 4.componentDidMount 组件挂载完成
  Counter 4.componentDidMount 组件挂载完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 5.componentWillReceiveProps 组件将要接收新的props
  ChildCounter 6.shouldComponentUpdate 询问组件是否需要更新?
  Counter 7.componentDidUpdate 组件更新完成
+ Counter 5.shouldComponentUpdate 询问组件是否需要更新?  
  • 第四次点击

第四次点击时,父组件走shouldComponentUpdate,此时num=4是2的倍数,返回的是true,父组件需要更新,调用componentWillUpdate父组件将要更新,从新render,这是因为num=4,{this.state.num===4?null:<ChildCounter num={this.state.num}/>}执行后,子组件被卸载,所以子组件走了componentWillUnmount,父组件继续执行componentDidUpdate

  Counter 1.constructor 属性,状态初始化
  Counter 2.componentWillMount 组件将要挂载
  Counter 3.render 生成虚拟DOM
  ChildCounter 1.constructor 属性,状态初始化
  ChildCounter 2.componentWillMount 组件将要挂载
  ChildCounter 3.render 生成虚拟DOM
  ChildCounter 4.componentDidMount 组件挂载完成
  Counter 4.componentDidMount 组件挂载完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 5.componentWillReceiveProps 组件将要接收新的props
  ChildCounter 6.shouldComponentUpdate 询问组件是否需要更新?
  Counter 7.componentDidUpdate 组件更新完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
+ Counter 5.shouldComponentUpdate 询问组件是否需要更新?
+ Counter 6.componentWillUpdate 组件将要更新
+ Counter 3.render 生成虚拟DOM
+ ChildCounter 9.componentWillUnmount 组件将被卸载
+ Counter 7.componentDidUpdate 组件更新完成
  • 第五次点击

第五次点击时父组,件走了shouldComponentUpdate询问是否需要更新组件,此时num=5,返回的false,父组件不更新,子组件也不会更新

  Counter 1.constructor 属性,状态初始化
  Counter 2.componentWillMount 组件将要挂载
  Counter 3.render 生成虚拟DOM
  ChildCounter 1.constructor 属性,状态初始化
  ChildCounter 2.componentWillMount 组件将要挂载
  ChildCounter 3.render 生成虚拟DOM
  ChildCounter 4.componentDidMount 组件挂载完成
  Counter 4.componentDidMount 组件挂载完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 5.componentWillReceiveProps 组件将要接收新的props
  ChildCounter 6.shouldComponentUpdate 询问组件是否需要更新?
  Counter 7.componentDidUpdate 组件更新完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 9.componentWillUnmount 组件将被卸载
  Counter 7.componentDidUpdate 组件更新完成
+ Counter 5.shouldComponentUpdate 询问组件是否需要更新?  
  • 第六次点击

第六次点击时,父组件走shouldComponentUpdate,此时num=6是2的倍数,返回的是true,父组件需要更新,调用componentWillUpdate父组件将要更新,从新render,由于之前在子组件被卸载了,需要重新开始,因为需要执行子组件constructor->子组件componentWillMount->子组件render->子组件componentDidMount,再执行父组件componentDidMount

 	Counter 1.constructor 属性,状态初始化
  Counter 2.componentWillMount 组件将要挂载
  Counter 3.render 生成虚拟DOM
  ChildCounter 1.constructor 属性,状态初始化
  ChildCounter 2.componentWillMount 组件将要挂载
  ChildCounter 3.render 生成虚拟DOM
  ChildCounter 4.componentDidMount 组件挂载完成
  Counter 4.componentDidMount 组件挂载完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 5.componentWillReceiveProps 组件将要接收新的props
  ChildCounter 6.shouldComponentUpdate 询问组件是否需要更新?
  Counter 7.componentDidUpdate 组件更新完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 9.componentWillUnmount 组件将被卸载
  Counter 7.componentDidUpdate 组件更新完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
+ Counter 5.shouldComponentUpdate 询问组件是否需要更新?
+ Counter 6.componentWillUpdate 组件将要更新
+ Counter 3.render 生成虚拟DOM
+ ChildCounter 1.constructor 属性,状态初始化
+ ChildCounter 2.componentWillMount 组件将要挂载
+ ChildCounter 3.render 生成虚拟DOM
+ ChildCounter 4.componentDidMount 组件挂载完成
+ Counter 7.componentDidUpdate 组件更新完成
  • 第七次点击

第七次点击时父组,件走了shouldComponentUpdate询问是否需要更新组件,此时num=7,返回的false,父组件不更新,子组件也不会更新

 	Counter 1.constructor 属性,状态初始化
  Counter 2.componentWillMount 组件将要挂载
  Counter 3.render 生成虚拟DOM
  ChildCounter 1.constructor 属性,状态初始化
  ChildCounter 2.componentWillMount 组件将要挂载
  ChildCounter 3.render 生成虚拟DOM
  ChildCounter 4.componentDidMount 组件挂载完成
  Counter 4.componentDidMount 组件挂载完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 5.componentWillReceiveProps 组件将要接收新的props
  ChildCounter 6.shouldComponentUpdate 询问组件是否需要更新?
  Counter 7.componentDidUpdate 组件更新完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 9.componentWillUnmount 组件将被卸载
  Counter 7.componentDidUpdate 组件更新完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 1.constructor 属性,状态初始化
  ChildCounter 2.componentWillMount 组件将要挂载
  ChildCounter 3.render 生成虚拟DOM
  ChildCounter 4.componentDidMount 组件挂载完成
  Counter 7.componentDidUpdate 组件更新完成
+ Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  • 第八次点击

第八次点击时父组,父组件走shouldComponentUpdate,此时num=8是2的倍数,返回的是true,父组件需要更新,调用componentWillUpdate父组件将要更新,从新render,此时子组件的componentWillReceiveProps被调用,子组件将要接收新的props,接着子组件的shouldComponentUpdate被调用,询问子组件是否需要更新,此时num=8,不是3的倍数,返回的是false,子组件不更新,接着执行componentDidUpdate父组件更新完成

 	Counter 1.constructor 属性,状态初始化
  Counter 2.componentWillMount 组件将要挂载
  Counter 3.render 生成虚拟DOM
  ChildCounter 1.constructor 属性,状态初始化
  ChildCounter 2.componentWillMount 组件将要挂载
  ChildCounter 3.render 生成虚拟DOM
  ChildCounter 4.componentDidMount 组件挂载完成
  Counter 4.componentDidMount 组件挂载完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 5.componentWillReceiveProps 组件将要接收新的props
  ChildCounter 6.shouldComponentUpdate 询问组件是否需要更新?
  Counter 7.componentDidUpdate 组件更新完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 9.componentWillUnmount 组件将被卸载
  Counter 7.componentDidUpdate 组件更新完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 1.constructor 属性,状态初始化
  ChildCounter 2.componentWillMount 组件将要挂载
  ChildCounter 3.render 生成虚拟DOM
  ChildCounter 4.componentDidMount 组件挂载完成
  Counter 7.componentDidUpdate 组件更新完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
+ Counter 5.shouldComponentUpdate 询问组件是否需要更新?
+ Counter 6.componentWillUpdate 组件将要更新
+ Counter 3.render 生成虚拟DOM
+ ChildCounter 5.componentWillReceiveProps 询问组件是否需要更新?
+ ChildCounter 6.shouldComponentUpdate 询问组件是否需要更新?
+ Counter 7.componentDidUpdate 组件更新完成
  • 第九次点击

第九次点击时父组,件走了shouldComponentUpdate询问是否需要更新组件,此时num=9,返回的false,父组件不更新,子组件也不会更新

  Counter 1.constructor 属性,状态初始化
  Counter 2.componentWillMount 组件将要挂载
  Counter 3.render 生成虚拟DOM
  ChildCounter 1.constructor 属性,状态初始化
  ChildCounter 2.componentWillMount 组件将要挂载
  ChildCounter 3.render 生成虚拟DOM
  ChildCounter 4.componentDidMount 组件挂载完成
  Counter 4.componentDidMount 组件挂载完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 5.componentWillReceiveProps 组件将要接收新的props
  ChildCounter 6.shouldComponentUpdate 询问组件是否需要更新?
  Counter 7.componentDidUpdate 组件更新完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 9.componentWillUnmount 组件将被卸载
  Counter 7.componentDidUpdate 组件更新完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 1.constructor 属性,状态初始化
  ChildCounter 2.componentWillMount 组件将要挂载
  ChildCounter 3.render 生成虚拟DOM
  ChildCounter 4.componentDidMount 组件挂载完成
  Counter 7.componentDidUpdate 组件更新完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 5.componentWillReceiveProps 询问组件是否需要更新?
  ChildCounter 6.shouldComponentUpdate 询问组件是否需要更新?
  Counter 7.componentDidUpdate 组件更新完成
+ Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  • 第十次点击

第十次点击时父组,父组件走shouldComponentUpdate,此时num=10是2的倍数,返回的是true,父组件需要更新,调用componentWillUpdate父组件将要更新,从新render,此时子组件的componentWillReceiveProps被调用,子组件将要接收新的props,接着子组件的shouldComponentUpdate被调用,询问子组件是否需要更新,此时num=10,不是3的倍数,返回的是false,子组件不更新,接着执行componentDidUpdate父组件更新完成

  Counter 1.constructor 属性,状态初始化
  Counter 2.componentWillMount 组件将要挂载
  Counter 3.render 生成虚拟DOM
  ChildCounter 1.constructor 属性,状态初始化
  ChildCounter 2.componentWillMount 组件将要挂载
  ChildCounter 3.render 生成虚拟DOM
  ChildCounter 4.componentDidMount 组件挂载完成
  Counter 4.componentDidMount 组件挂载完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 5.componentWillReceiveProps 组件将要接收新的props
  ChildCounter 6.shouldComponentUpdate 询问组件是否需要更新?
  Counter 7.componentDidUpdate 组件更新完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 9.componentWillUnmount 组件将被卸载
  Counter 7.componentDidUpdate 组件更新完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 1.constructor 属性,状态初始化
  ChildCounter 2.componentWillMount 组件将要挂载
  ChildCounter 3.render 生成虚拟DOM
  ChildCounter 4.componentDidMount 组件挂载完成
  Counter 7.componentDidUpdate 组件更新完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 5.componentWillReceiveProps 询问组件是否需要更新?
  ChildCounter 6.shouldComponentUpdate 询问组件是否需要更新?
  Counter 7.componentDidUpdate 组件更新完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
+ Counter 5.shouldComponentUpdate 询问组件是否需要更新?
+ Counter 6.componentWillUpdate 组件将要更新
+ Counter 3.render 生成虚拟DOM
+ ChildCounter 5.componentWillReceiveProps 询问组件是否需要更新?
+ ChildCounter 6.shouldComponentUpdate 询问组件是否需要更新?
+ Counter 7.componentDidUpdate 组件更新完成
  • 第十一次点击

第十一次点击时父组,件走了shouldComponentUpdate询问是否需要更新组件,此时num=11,返回的false,父组件不更新,子组件也不会更新

  Counter 1.constructor 属性,状态初始化
  Counter 2.componentWillMount 组件将要挂载
  Counter 3.render 生成虚拟DOM
  ChildCounter 1.constructor 属性,状态初始化
  ChildCounter 2.componentWillMount 组件将要挂载
  ChildCounter 3.render 生成虚拟DOM
  ChildCounter 4.componentDidMount 组件挂载完成
  Counter 4.componentDidMount 组件挂载完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 5.componentWillReceiveProps 组件将要接收新的props
  ChildCounter 6.shouldComponentUpdate 询问组件是否需要更新?
  Counter 7.componentDidUpdate 组件更新完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 9.componentWillUnmount 组件将被卸载
  Counter 7.componentDidUpdate 组件更新完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 1.constructor 属性,状态初始化
  ChildCounter 2.componentWillMount 组件将要挂载
  ChildCounter 3.render 生成虚拟DOM
  ChildCounter 4.componentDidMount 组件挂载完成
  Counter 7.componentDidUpdate 组件更新完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 5.componentWillReceiveProps 询问组件是否需要更新?
  ChildCounter 6.shouldComponentUpdate 询问组件是否需要更新?
  Counter 7.componentDidUpdate 组件更新完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 5.componentWillReceiveProps 询问组件是否需要更新?
  ChildCounter 6.shouldComponentUpdate 询问组件是否需要更新?
  Counter 7.componentDidUpdate 组件更新完成
+ Counter 5.shouldComponentUpdate 询问组件是否需要更新?  
  • 第十二次点击

第十二次点击时父组,父组件走shouldComponentUpdate,此时num=12是2的倍数,返回的是true,父组件需要更新,调用componentWillUpdate父组件将要更新,从新render,此时子组件的componentWillReceiveProps被调用,子组件将要接收新的props,接着子组件的shouldComponentUpdate被调用,询问子组件是否需要更新,此时num=12,是3的倍数,返回的是true,调用子组件componentWillUpdate->子组件render->子组件componentDidUpdate,接着执行componentDidUpdate父组件更新完成

  Counter 1.constructor 属性,状态初始化
  Counter 2.componentWillMount 组件将要挂载
  Counter 3.render 生成虚拟DOM
  ChildCounter 1.constructor 属性,状态初始化
  ChildCounter 2.componentWillMount 组件将要挂载
  ChildCounter 3.render 生成虚拟DOM
  ChildCounter 4.componentDidMount 组件挂载完成
  Counter 4.componentDidMount 组件挂载完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 5.componentWillReceiveProps 组件将要接收新的props
  ChildCounter 6.shouldComponentUpdate 询问组件是否需要更新?
  Counter 7.componentDidUpdate 组件更新完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 9.componentWillUnmount 组件将被卸载
  Counter 7.componentDidUpdate 组件更新完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 1.constructor 属性,状态初始化
  ChildCounter 2.componentWillMount 组件将要挂载
  ChildCounter 3.render 生成虚拟DOM
  ChildCounter 4.componentDidMount 组件挂载完成
  Counter 7.componentDidUpdate 组件更新完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 5.componentWillReceiveProps 询问组件是否需要更新?
  ChildCounter 6.shouldComponentUpdate 询问组件是否需要更新?
  Counter 7.componentDidUpdate 组件更新完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?
  Counter 6.componentWillUpdate 组件将要更新
  Counter 3.render 生成虚拟DOM
  ChildCounter 5.componentWillReceiveProps 询问组件是否需要更新?
  ChildCounter 6.shouldComponentUpdate 询问组件是否需要更新?
  Counter 7.componentDidUpdate 组件更新完成
  Counter 5.shouldComponentUpdate 询问组件是否需要更新?  
+ Counter 5.shouldComponentUpdate 询问组件是否需要更新?
+ Counter 6.componentWillUpdate 组件将要更新
+ Counter 3.render 生成虚拟DOM
+ ChildCounter 5.componentWillReceiveProps 询问组件是否需要更新?
+ ChildCounter 6.shouldComponentUpdate 询问组件是否需要更新?
+ ChildCounter 7.componentWillUpdate 组件将要更新
+ ChildCounter 3.render 生成虚拟DO
+ ChildCounter 8.componentDidUpdate 组件更新完成
+ Counter 7.componentDidUpdate 组件更新完成

6.3.全生命周期(dom-diff)

6.3.1.src/Component.js
import { compareTwoVdom ,findDOM} from './react-dom'

//更新队列
export let updateQueue = {
    isBatchingUpdate: false,//当前是否处于批量更新模式
+   updaters: [],
    batchUpdate() {//批量更新
        for (let updater of this.updaters) {
            updater.updateClassComponent();
        }
        this.isBatchingUpdate = false;
+       this.updaters.length=0;
    }
}
//更新器
class Updater {
    constructor(classInstance) {
        this.classInstance = classInstance;//类组件的实例
        this.pendingStates = [];//等待生效的状态,可能是一个对象,也可能是一个函数
        this.cbs = [];//存放回调
    }

    /**
     * 
     * @param {*} partialState 等待更新生效的状态
     * @param {*} cb 状态更新的回调
     */
    addState(partialState, cb) {
        this.pendingStates.push(partialState);
        typeof cb === 'function' && this.cbs.push(cb);
        this.emitUpdate();
    }

    //一个组件不管属性变了,还是状态变了,都会更新
    emitUpdate(nextProps) {
+       this.nextProps = nextProps;//缓存起来
        if (updateQueue.isBatchingUpdate) {//当前处于批量更新模式,先缓存updater
            updateQueue.updaters.add(this);//本次setState调用结束
        } else {//当前处于非批量更新模式,执行更新
            this.updateClassComponent();//直接更新组件
        }
    }

    updateClassComponent() {
+       const { classInstance, pendingStates, cbs, nextProps } = this;
+       if (nextProps || pendingStates.length > 0) {//有setState
+           shouldUpdate(classInstance, nextProps, this.getState())
            // classInstance.state = this.getState();//计算新状态
            // classInstance.forceUpdate();
            // cbs.forEach(cb=>cb());
            // cbs.length=0;
        }
    }

    getState() {//计算新状态
        const { classInstance, pendingStates } = this;
        let { state } = classInstance;//获取老状态
        pendingStates.forEach(newState => {
            //newState可能是对象,也可能是函数,对象setState的两种方式
            if (typeof newState === 'function') {
                newState = newState(state);
            }
            state = { ...state, ...newState };
        })
        pendingStates.length = 0;//清空数组
        return state;
    }
}

/**
 * 判断组件是否需要更新
 * @param {*} classInstance 类组件实例
 * @param {*} nextProps 新的props
 * @param {*} newState 新状态
 */
function shouldUpdate(classInstance, nextProps, newState) {
+    let willUpdate = true;//是否需要更新
+   //如果有这个方法,并且这个方法的返回值为false,则不需要继续向下更新了,否则就更新
+   if (classInstance.shouldComponentUpdate && !classInstance.shouldComponentUpdate(nextProps, newState)) {
+       willUpdate = false;
+   }
+   //如果需要更新,并且组件调用类componentWillUpdate方法
+   if (willUpdate && classInstance.componentWillUpdate) {
+       classInstance.componentWillUpdate();//执行生命周期方法componentWillUpdate
+   }
+   //不管是否需要更新,属性和状态都有改变
+   if (nextProps) {
+       classInstance.props = nextProps;
+   }
+   //不管组件要不要更新,组件的state一定会改变
+   classInstance.state = newState;
+   //如果需要更新,走组件的更新逻辑
+   willUpdate && classInstance.forceUpdate()
}
  
class Component {
    //用来判断是类组件
    static isReactComponent = true;
    constructor(props) {
        this.props = props;
        this.state = {};
        //每个类组件都有一个更新器
        this.updater = new Updater(this);
    }

    setState(partialState, cb) {
        this.updater.addState(partialState, cb);
    }

    forceUpdate() {
        const newRenderVdom = this.render();//新的虚拟DOM
+       const oldRenderVdom = this.oldRenderVdom;//老得虚拟DOM
+       const dom = findDOM(oldRenderVdom);//老得真实DOM
+       compareTwoVdom(dom.parentNode, oldRenderVdom, newRenderVdom);
+       this.oldRenderVdom=newRenderVdom;//比较完毕后,重新赋值老的虚拟节点
+       //调用生命周期方法componentDidUpdate
+       this.componentDidUpdate && this.componentDidUpdate();
    }
}

export default Component;
6.3.2.src/event.js
/*
 * @Author: dfh
 * @Date: 2021-02-25 15:54:59
 * @LastEditors: dfh
 * @LastEditTime: 2021-02-28 15:59:27
 * @Modified By: dfh
 * @FilePath: /day25-react/src/event.js
 */
import { updateQueue } from './Component';

/**
 * 给真实DOM添加事件处理函数
 * 为什么要这么做,为什么要做事件委托?
 *  1.可以做兼容处理,兼容不同浏览器,不能的浏览器event是不一样的, 处理浏览器的兼容性
 *  2.可以在事件处理函数之前和之后做一些事情,比如:
 *     2.1 之前 updateQueue.isBatchingUpdate=true
 *     2.2 之后 updateQueue.batchUpdate
 * @param {*} dom 真实DOM
 * @param {*} eventType 事件类型
 * @param {*} listener 监听函数
 */
export function addEvent(dom, eventType, listener) {
    const store = dom.store || (dom.store = {});
    store[eventType] = listener;//store.onclick=handlerClick
    if (!document[eventType]) {
        //事件委托,不管你给哪个DOM元素上绑事件,最后都统一代理到document上去了
        document[eventType] = dispatchEvent;//document.onclick=dispatchEvent
    }
}

let syntheticEvent = {}
/**
 * 
 * @param {*} event 原生event
 */
function dispatchEvent(event) {
    //target事件源button ,type类型click
    let { target, type } = event;
    const eventType = `on${type}`;
    updateQueue.isBatchingUpdate = true;//设置为批量更新模式
    createSyntheticEvent(event);
    while (target) {//事件冒泡
        const { store } = target;
        const listener = store && store[eventType];
        listener && listener.call(target, syntheticEvent);
        target = target.parentNode;
    }

    //syntheticEvent使用后清空
    for (const key in syntheticEvent) {
        syntheticEvent[key] = null;
    }
+   updateQueue.isBatchingUpdate = false;
    updateQueue.batchUpdate();//批量更新
}

/**
 * 将元素事件都拷贝到syntheticEvent上
 * @param {*} nativeEvent 元素事件
 */
function createSyntheticEvent(nativeEvent) {
    for (const key in nativeEvent) {
        syntheticEvent[key] = nativeEvent[key];
    }
}
6.3.3.src/constants.js
+ export const REACT_TEXT = Symbol('REACT_TEXT');
6.3.4.src/react-dom.js
+ import { REACT_TEXT } from './constants';
  import { addEvent } from './event';

/**
 * 给跟容器挂载的时候
 * @param {*} vdom 需要渲染的虚拟DOM
 * @param {*} container 容器
 */
function render(vdom, container) {
    const dom = createDOM(vdom);
    //挂载真实DOM
    container.appendChild(dom);
    //调用生命周期方法componentDidMount
    dom.componentDidMount && dom.componentDidMount();
}

/**
 * 创建证实DOM
 * @param {*} vdom 虚拟DOM
 */
export function createDOM(vdom) {
    const {
        type,
        props } = vdom;
    //创建真实DOM
    let dom;
+   if (type === REACT_TEXT) {//是文本
+       dom = document.createTextNode(props.content);
+   } else if (typeof type === 'function') {//自定义函数组件
        if (type.isReactComponent) {//类组件
            return mountClassComponent(vdom);
        } else {//函数组件
            return mountFunctionComponent(vdom);
        }
    } else {//原生组件
        dom = document.createElement(type);
    }

    //使用虚拟DOM的属性更新刚创建出来的真实DOM的属性
+   updateProps(dom, {}, props);
    if (typeof props.children === 'object' && props.children.type) {//只有一个儿子,并且是虚拟DOM
        render(props.children, dom);//把儿子变成真实DOM,并且挂载到自己身上
    } else if (Array.isArray(props.children)) {//有多个儿子
        reconcileChildren(props.children, dom);
    }

    //将真实DOM挂载到虚拟DOM上,以便后面取
+   vdom.dom = dom;
    return dom;
}

/**
 * 把一个类型为自定义类组件的虚拟DOM转化为一个真实DOM并返回
 * @param {*} vdom 类型为自定义类组件的虚拟DOM
 */
function mountClassComponent(vdom) {
    const { type: Clazz, props } = vdom;
    //获取类的实例
    const classInstance = new Clazz(props);
    //让这个类组件的虚拟DOM的classInstance属性指向这个类组件的实例
+   vdom.classInstance = classInstance;
    //调用生命周期方法componentWillMount
    if (classInstance.componentWillMount) {
        classInstance.componentWillMount();
    }
    //获取虚拟DOM
    const oldRenderVdom = classInstance.render();
    //将虚拟DOM挂载的组件实例上,以便后面DOM-diff时用
+   classInstance.oldRenderVdom = vdom.oldRenderVdom = oldRenderVdom;
    //获取真实DOM
    const dom = createDOM(oldRenderVdom);

    if (classInstance.componentDidMount) {
        dom.componentDidMount = classInstance.componentDidMount;
    }
    //将真实dom挂到实例上上
    classInstance.dom = dom;
    return dom;
}

/**
 * 把一个类型为自定义函数组件的虚拟DOM转换为一个真实DOM并返回
 * @param {*} vdom 类型为自定义函数组件的虚拟DOM
 */
function mountFunctionComponent(vdom) {
    const { type: FunctionComponent, props } = vdom;
    const renderVdom = FunctionComponent(props);
+   vdom.oldRenderVdom = renderVdom;
    return createDOM(renderVdom);
}

/**
 * 
 * @param {*} childrenVdom 孩子门的虚拟DOM
 * @param {*} parentDOM 要挂载到的真实DOM
 */
function reconcileChildren(childrenVdom, parentDOM) {
    for (let i = 0; i < childrenVdom.length; i++) {
        const child = childrenVdom[i];
        render(child, parentDOM);//把儿子挂载的自己身上
    }
}
/**
 * 使用虚拟DOM的属性更新刚创建出来的真实DOM的属性
 * @param {*} dom 真实DOM
 * @param {*} props 虚拟DOM属性
 */
function updateProps(dom, oldProps, props) {
    for (const key in props) {
        if (key === 'children') continue;//单独处理,不再此处处理
        if (key === 'style') {
            const styleObj = props.style;
            for (const attr in styleObj) {
                dom.style[attr] = styleObj[attr];
            }
        } else if (key.startsWith('on')) {//onClick=>onclick
            // dom[key.toLocaleLowerCase()]=props[key];
            addEvent(dom, key.toLocaleLowerCase(), props[key]);
        } else {//在JS中定义class使用的是className,所以不要改
            dom[key] = props[key];
        }
    }
}

/**
 * 对当前组件进行DOM-DIFF
 * @param {*} parentDOM 老得父真实DOM
 * @param {*} oldRenderVdom 老得虚拟DOM
 * @param {*} newRenderVdom 新的虚拟DOM
 * @param {*} nextDom 下一个真实DOM,主要用来插入找位置用
 */
+ export function compareTwoVdom(parentDOM, oldRenderVdom, newRenderVdom, nextDom) {
+   if (!oldRenderVdom && !newRenderVdom) {//新老虚拟DOM都为null
+       return null;
+   } else if (oldRenderVdom && !newRenderVdom) {//新的虚拟DOM为NULL,老得存在
+       const currentDOM = findDOM(oldRenderVdom);//找到此虚拟DOM对应的真实DOM
+       currentDOM && parentDOM.removeChild(currentDOM);//移除此老得真实DOM
+       //调用生命周期方法
+       oldRenderVdom.classInstance && oldRenderVdom.classInstance.componentWillUnmount &&  oldRenderVdom.classInstance.componentWillUnmount()
+   } else if (!oldRenderVdom && newRenderVdom) {//新的虚拟DOM存在,老得虚拟DOM为NULL
+       const newDOM = createDOM(newRenderVdom);//获取真实DOM
+       if (nextDom) {
+           parentDOM.insertBefore(newDOM, nextDom);
+       } else {
+           parentDOM.appendChild(newDOM);
+       }
+   } else if (oldRenderVdom && newRenderVdom && oldRenderVdom.type !== newRenderVdom.type) {//新老虚拟DOM都存在,但是类型不同
+       const oldDOM = findDOM(oldRenderVdom);//老得真实DOM
+       const newDOM = createDOM(newRenderVdom);//新的真实DOM
+       parentDOM.replaceChild(newDOM, oldDOM);
+       //调用生命周期方法
+       oldRenderVdom.classInstance && oldRenderVdom.classInstance.componentWillUnmount && oldRenderVdom.classInstance.componentWillUnmount()
+   } else {//新老都有,类型也一样,要进行深度DOM-DIFF
+       updateElement(oldRenderVdom, newRenderVdom);
+   }
+ }

/**
 * 深度对比两个虚拟DOM
 * @param {*} oldRenderVdom 老得虚拟DOM
 * @param {*} newRenderVdom 新的虚拟DOM
 */
+ function updateElement(oldRenderVdom, newRenderVdom) {
+   if (oldRenderVdom.type === REACT_TEXT) {//文本
+       const currentDOM = newRenderVdom.dom = oldRenderVdom.dom;//复用老得真实DOM节点
+       currentDOM.textContent = newRenderVdom.props.content;//直接修改老的DOM节点的文件就可以了
+   } else if (typeof oldRenderVdom.type === 'string') {//说明是一个原生组件
+       const currentDOM = newRenderVdom.dom = oldRenderVdom.dom;//复用老得真实DOM
+       //先更新属性
+       updateProps(currentDOM, oldRenderVdom.props, newRenderVdom.props);
+       //比较儿子们
+       updateChildren(currentDOM, oldRenderVdom.props.children, newRenderVdom.props.children);
+   } else if (typeof oldRenderVdom.type === 'function') {
+       if (oldRenderVdom.type.isReactComponent) {
+           updateClassComponent(oldRenderVdom, newRenderVdom);//老新都是类组件,进行类组件更新
+       } else {
+           updateFunctionComponent(oldRenderVdom, newRenderVdom);//新老都是函数组件,进行函数组件更新
+       }
+   }
+ }

/**
 *  如果老得虚拟DOM节点和新的虚拟DOM节点都是函数的话,走这个更新逻辑
 * @param {*} oldVdom 老得虚拟DOM
 * @param {*} newVdom 新的虚拟DOM
 */
+ function updateFunctionComponent(oldVdom, newVdom) {
+   const parentDOM = findDOM(oldVdom).parentNode;//找到老得父节点
+   const { type: FunctionComponent, props } = newVdom;
+   const oldRenderVdom = oldVdom.oldRenderVdom;//老得的渲染虚拟DOM
+   const newRenderVdom = FunctionComponent(props);//新的渲染虚拟DOM
+   compareTwoVdom(parentDOM, oldRenderVdom, newRenderVdom);//比较虚拟DOM
+   newVdom.oldRenderVdom = newRenderVdom;
+ }

/**
 * 如果老得虚拟DOM节点和新的虚拟DOM节点都是类组件的话,走这个更新逻辑
 * @param {*} oldVdom 老得虚拟DOM
 * @param {*} newVdom 新的虚拟DOM
 */
+ function updateClassComponent(oldVdom, newVdom) {
+   const classInstance = newVdom.classInstance = oldVdom.classInstance;//复用老得类的实例
+   newVdom.oldRenderVdom = oldVdom.oldRenderVdom;//上一次类组件的渲染出来的虚拟DOM
+   if (classInstance.componentWillReceiveProps) {//组件将要接受到新的属性
+       classInstance.componentWillReceiveProps();
+   }
+   //触发组件的更新,把新的属性传递过去
+   classInstance.updater.emitUpdate(newVdom.props);
+ }
/**
 * 深度比较孩子们
 * @param {*} parentDOM 父DOM 
 * @param {*} oldChildren 老得儿子们
 * @param {*} newChildren 新的儿子们
 */
+ function updateChildren(parentDOM, oldChildren, newChildren) {
+   //孩子可能是数组或者对象(单节点是对象)
+   oldChildren = Array.isArray(oldChildren) ? oldChildren : [oldChildren];
+   newChildren = Array.isArray(newChildren) ? newChildren : [newChildren];
+   //获取最大的长度
+   const maxLen = Math.max(oldChildren.length, newChildren.length);
+   for (let i = 0; i < maxLen; i++) {
+       //在儿子们里查找,找到索引大于当前索引的
+       const nextDOM = oldChildren.find((item, index) => index > i && item && item.dom)
+       //递归比较孩子
+       compareTwoVdom(parentDOM, oldChildren[i], newChildren[i], nextDOM && nextDOM.dom);
+   }
+ }


/**
 * 查找此虚拟DOM对象的真实DOM
 * @param {*} vdom 
 */
+ export function findDOM(vdom) {
+   const { type } = vdom;
+   let dom;
+   if (typeof type === 'function') {
+       dom = findDOM(vdom.oldRenderVdom)
+   } else {
+       dom = vdom.dom;
+   }
+   return dom
+ }

const ReactDOM = {
    render
}

export default ReactDOM;
6.3.5.src/react.js
  import Component from './Component';
+ import { wrapToVdom } from './utils';
/**
 * 
 * @param {*} type 元素类型
 * @param {*} config 配置对象
 * @param {*} children 孩子或者孩子门
 */
function createElement(type, config, children) {
    if (config) {
        delete config.__source;
        delete config.__self;
    }
    let props = { ...config };

    if (arguments.length > 3) {//children是一个数组
+       props.children = Array.prototype.slice.call(arguments, 2).map(wrapToVdom);
+   } else {
+       props.children = wrapToVdom(children);
    }
    return {
        type,
        props
    }
}

const React = {
    createElement,
    Component
}
export default React;
6.3.6.src/utils.js
+ import { REACT_TEXT } from './constants';

/**
 * 为了后面的DOM-DIFF,我把文本节点进行单独的封装或者说标识
 * 不管你原来是什么,都全部包装成React元素的形式。
 * @param {*} element 可能是一个对象,也可能是一个常量
 */
+ export function wrapToVdom(element){
+   return (typeof element === 'string'||typeof element === 'number')
+   ?{type:REACT_TEXT,props:{content:element}}:element;
+ }