React

react之setState的异步及合并执行更新组件

2020-04-16  本文已影响0人  Mr无愧于心

前言须知

  1. jsx的渲染过程
    jsx会经过babel编译成createElement函数的结构,然后createElement执行产生虚拟dom结构VNode(就是一个有一定属性的对象结构),然后通过rander函数处理VNode为虚拟节点,在页面中渲染。详细请点击此处

  2. dom的更新过程
    diff算法对比新旧VNode,如果新旧VNode不一样就调用rander重新渲染视图的过程。详细请点击此处

回归正题

setState的使用
为了避免每执行一次 setState,就重新生成 newVNode 进行 diff。,会造成主进程的阻塞,页面的卡死,所以setState做了异步处理合并执行(多次连续调用会被最终合并成一次)的两种优化。
异步处理的实现可以通过promise来实现

let updateQueue=[];
function enqueueRender(updater) { 
    // 将所有 updater 同步推入更新队列中
    // 为实例添加一个属性 __dirty,标识是否处于待更新状态
    // 初始 和 更新完毕,该值会被置为 false
    // 推入队列时,标记为 true
    if (
        !updater.__dirty && 
        (updater.__dirty = true) && 
        updateQueue.push(updater) === 1//添加入队列
    ) {
        // 异步化冲洗队列(微任务)
        // 最终只执行一次冲洗
        // 合并一次循环中多次 updater
      new Promise().then(()=>{
        if (updateQueue.length) {
          updateQueue.sort()
          let curUpdater = updateQueue.pop()
          while (curUpdater) {
            if (curUpdater.__dirty) {
              // 当组件处于 待更新态 时,触发组件更新
              // 如果该组件已经被更新完毕,则该状态为 false
              // 则后续的更新均不再执行
              curUpdater.__update()//更新组件  在组件更新完毕设置this.__dirty = false
              //处理callback回调函数
              const callbacks = curUpdater.__setStateCallbacks
              let cbk
              if (callbacks && callbacks.length) {
                while (cbk = callbacks.shift()) cbk.call(curUpdater)
              }   
            }
            curUpdater = updateQueue.pop()
          }
      })
   }
}

setState(partialState = {}, callback?) {
  if (typeof partialState === 'function') {
    partialState = partialState(this.state, this.props)
  }
        
  this.__nextState = {
    ...this.state,
    ...partialState,
  }
    // 缓存回调
  callback && this.__setStateCallbacks.push(callback)
  // 把组件自身先推入更新队列
  enqueueUpdate(this)
}
__update(){
  diff(oldVNode, newVNode);
  render();
}
function diff(oldVNode, newVNode) {
    if (isSameVNode(oldVNode, newVNode)) {
        if (typeof oldVNode.type === 'function') {
            // 组件节点
            diffComponent(oldVNode, newVNode)
        } else {
            // 元素节点,
            // 直接执行比对
            diffVNode(oldVNode, newVNode)
        }
    } else {
        // 新节点替换旧节点
        ...
    }
}

// 组件比对
function diffComponent(oldCompVNode, newCompVNode) {
    const { instance, vnode: oldVNode, elm } = oldCompVNode
    const { props: nextProps } = newCompVNode
    if (instance && oldVNode) {
        instance.__dirty = false
        // 更新状态和属性
        instance.__nextProps = nextProps
        if (!instance.__nextState) instance.__nextState = instance.state        
        // 复用旧组件实例和元素
        newCompVNode.instance = instance
        newCompVNode.elm = elm
        // 使用新属性、新状态,旧组件实例
        // 重新生成 新虚拟DOM
        const newVNode = initComponent(newCompVNode)       
        // 递归触发 diff
        diff(oldVNode, newVNode)
    }
}

由于更新队列为异步的,因此当多次连续调用 setState 时,组件的状态会被 同步合并,待全部完成后,才会进入更新队列的冲洗并最终只执行一次组件更新


本文参考:https://juejin.im/post/5e65a258f265da57133b37cc#heading-7

上一篇 下一篇

猜你喜欢

热点阅读