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

前言须知

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

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

回归正题

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

  • setState的实现
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虚拟DOM
    重新渲染组件
__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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,176评论 5 469
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,190评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,232评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,953评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,879评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,177评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,626评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,295评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,436评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,365评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,414评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,096评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,685评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,771评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,987评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,438评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,032评论 2 341

推荐阅读更多精彩内容