• $nextTick 的原理源码解读


    $nextTick 的原理

    Vue 是异步修改 DOM 的并且不鼓励开发者直接接触 DOM,但有时候业务需要必须对数据更改–刷新后的 DOM 做相应的处理,这时候就可以使用 Vue.nextTick(callback)这个 api 了。

    nextTick 回调函数先后执行的方式。如果大家看过这部分的源码,会发现其中做了很多 isNative()的判断,因为这里还存在兼容性优雅降级的问题。可见 Vue 开发团队的深思熟虑,对性能的良苦用心。

    工程源码: src/core/util/next-tick.js

    // 储存 nextTick 的回调函数, 同一时期可能有多个地方会调用 $nextTick()
    const callbacks = []
    // 节流阀: 目的是同一时刻只开启一个 timerFunc
    let pending = false 
    // 定时函数
    let timerFunc 
    
    // 刷新队列的函数
    function flushCallbacks () {
      pending = false
      // 复制 callback
      const copies = callbacks.slice(0)
      // 清除 callback
      callbacks.length = 0
      for (let i = 0; i < copies.length; i++) {
      	// 以此调用 nextTick 回调
        copies[i]()
      }
    }
    
    // $nextTick 会依次判断是否支持 Promise => MutationObserver => setImmediate => setTimeout
    if (typeof Promise !== 'undefined' && isNative(Promise)) {
      // 如果支持 Promise, 就使用 Promise 实现
      const p = Promise.resolve()
      timerFunc = () => {
        p.then(flushCallbacks)
      }
    } else if (!isIE && typeof MutationObserver !== 'undefined') {
      // 如果 Promise 不支持, 但支持 MutationObserver
      let counter = 1
      const observer = new MutationObserver(flushCallbacks)
      const textNode = document.createTextNode(String(counter))
      observer.observe(textNode, {
        characterData: true
      })
      // 改变 counter 属性触发 flushCallbacks
      timerFunc = () => {
        counter = (counter + 1) % 2
        textNode.data = String(counter)
      }
    } else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
      // 上面两种都不支持, 用 setImmediate
      timerFunc = () => {
        setImmediate(flushCallbacks)
      }
    } else {
      // 都不支持使用 setTimeout
      timerFunc = () => {
        setTimeout(flushCallbacks, 0)
      }
    }
    
    export function nextTick (cb, ctx) {
      // 存入 nextTick 回调, 可存入多个
      callbacks.push(() => {
        cb && cb.call(ctx)
      })
      if (!pending) {
        pending = true
        timerFunc()
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    总结

    • Vue 中 $nextTick 的降级策略
      Promise > MutationObserver > setImmediate > setTimeout
    • 原理
      • 利用异步队列
    • 在每个 macro-task(宏任务) 运行完以后, UI 都会重新渲染, 那么在 micro-task (异步事件回调) 中就完成数据更新当前事件循环结束就可以得到最新的 UI 了
    • 反之如果新建一个 macro-task 来做数据更新, 那么渲染就会进行两次
  • 相关阅读:
    太经典了,阿里技术官“亲码”千页Redis源码笔记,差距不止一点点
    如何在海外通过A/B测试来优化应用
    CF1668A Direction Change
    HP惠普暗影精灵8P笔记本OMEN 17.3 英寸游戏本 17-ck1000(509V8AV)原厂Win11系统22H2
    聚观早报 | 中国茶申遗成功;特斯拉市值蒸发4个推特
    利用styleSheet,避免js手动频繁修改样式
    Verilog disable语句
    Ubuntu释放VirtualBox虚拟硬盘文件大小
    frp 从0搭建内网穿透
    大模型chatgpt4分析功能初探
  • 原文地址:https://blog.csdn.net/m0_62371650/article/details/128213966