Skip to content

11.getSnapshotBeforeUpdate源码实现

代码演化请观看视频,这里呈现相关代码:

js
// utils.js
export const deepClone = (data) => { 
    let type = getType(data); 
    let resultValue; 
    if (!(type === 'array' || type === 'object')) return data;
    if (type === 'array') { 
        resultValue = []; 
        data.forEach((item) => { 
            resultValue.push(deepClone(item)); 
        })    
        return resultValue; 
    } else if (type === 'object') { 
        resultValue = {}; 
        for (const key in data) { 
            if (data.hasOwnProperty(key)) { 
                resultValue[key] = deepClone(data[key]); 
            } 
        } 
        return resultValue; 
    } 
}

function getType(obj) {
    var toString = Object.prototype.toString;
    var map = {
      '[object Boolean]' : 'boolean', 
      '[object Number]'  : 'number', 
      '[object String]'  : 'string', 
      '[object Function]' : 'function', 
      '[object Array]'  : 'array', 
      '[object Date]'   : 'date', 
      '[object RegExp]'  : 'regExp', 
      '[object Undefined]': 'undefined',
      '[object Null]'   : 'null', 
      '[object Object]'  : 'object'
    };
    return map[toString.call(obj)];
  }
js
// Componet.js
import { deepClone } from './utils'
class Updater {
    constructor(ClassComponentInstance){
        this.ClassComponentInstance = ClassComponentInstance
        this.pendingStates = []
    }
    addState(partialState){
        this.pendingStates.push(partialState)
        this.preHandleForUpdate()
    }
    preHandleForUpdate(){
        if(updaterQueue.isBatch){
            updaterQueue.updaters.add(this)
        }else{
            this.launchUpdate()
        }
    }
    launchUpdate(nextProps){
        const { ClassComponentInstance, pendingStates } = this
        let prevProps = deepClone(this.ClassComponentInstance.props)///
        let prevState = deepClone(this.ClassComponentInstance.state)///
        if(pendingStates.length === 0  && !nextProps) return
        let isShouldUpdate = true;
        let nextState = this.pendingStates.reduce((preState, newState) => {
            return {
                ...preState, ...newState
            }
        }, this.ClassComponentInstance.state);
        
        if (ClassComponentInstance.shouldComponentUpdate && (!ClassComponentInstance.shouldComponentUpdate(nextProps, nextState))) {
            isShouldUpdate = false;
        }
        this.pendingStates.length = 0
        if(nextProps) ClassComponentInstance.props = nextProps
        ClassComponentInstance.state = nextState
        if(isShouldUpdate) ClassComponentInstance.update(prevProps, prevState) ///
    }
}
export class Component {
    static IS_CLASS_COMPONENT = true
    constructor(props){
        this.updater = new Updater(this)
        this.state = {}
        this.props = props
    }
    setState(partialState){
        // // 1.合并属性
        // this.state = {...this.state, ...partialState}
        // // 2.重新渲染进行更新
        // this.update()
        this.updater.addState(partialState)
    }
    update(prevProps, prevState){///
        // 1. 获取重新执行render函数后的虚拟DOM 新虚拟DOM
        // 2. 根据新虚拟DOM生成新的真实DOM
        // 3. 将真实DOM挂载到页面上
        let oldVNode = this.oldVNode; // TODO: 让类组件拥有一个oldVNode属性保存类组件实例对应的的虚拟DOM
        let oldDOM = findDomByVNode(oldVNode) // TODO: 将真实DOM保存到对应的虚拟DOM上  
        if (this.constructor.getDerivedStateFromProps) {
            let newState = this.constructor.getDerivedStateFromProps(this.props, this.state) || {};
            this.state = { ...this.state, ...newState };
        }
        let snapshot = this.getSnapshotBeforeUpdate && this.getSnapshotBeforeUpdate(prevProps, prevState);///
        let newVNode = this.render()
        updateDomTree(oldVNode, newVNode, oldDOM)
        this.oldVNode = newVNode
        if (this.componentDidUpdate) {
            this.componentDidUpdate(this.props, this.state, snapshot);///
        }
    }
}

基于 VitePress 构建