美文网首页
浅析 NextTick

浅析 NextTick

作者: 听一半的歌_f558 | 来源:发表于2019-08-05 13:57 被阅读0次

Next-tick

NextTick 是做什么到?

来自 Vue 官网讲述: 在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

是否似曾相似

Vue.nextTick(() => {})

this.$nextTick(() => {})

在 vue 中 created 函数钩子函数执行的时候DOM 其实并未进行任何渲染,所以得放在 nextTick 中去获取 dom,与其对于得生命周期钩子函数是 mounted

BUT 中 Vue 中 for 渲染 dom 就算是中 mounted 调用 nextTick也不能获取到具体到 dom,为什么

现在来一探究竟

先来看一下下面这个问题

打印的是什么,请先思考【下面有答案】


console.log(1)

setTimeout(() => {

  console.log(8)

}, 2000)

setTimeout(() => {

  console.log(3)

  Promise.resolve().then(() => {

    console.log(4)

  })

  setTimeout(() => {

    console.log(6)

  }, 3000)

}, 1000)

new Promise((resolve, reject) => {

  console.log(5)

}).then(() => {

  console.log(7)

})

console.log(2)

谈 Event-Loop

js 是单线程执行,当然,现在又有了一个 worker 创造了多线程环境,但是 worker 受限很多,

js 执行是有一个执行栈,主要分了,宏任务(macro-task)和 微任务(micro-task)

  • 宏任务有那些

    1. setTimeout

    2. I/O

    3. setInterval

    4. setImmediate

    5. 主线程

    6. MessageChannel

  • 微任务有那些

    1. Promise 系列 .then .catch .finally

    2. process.nexttick

    3. MutationObserver


// 执行流程

              (执行一个宏任务,产生宏任务,入栈)

                    ↑

--------↑------- 宏任务 ←--—--

        |          |        |

        |          |        |

        |          |        |

        |          ↓        | 没有

        |        微任务 ------

        |          |↘

        |          |(执行所有微任务过程中产生微任务,继续执行)

        |          |

        |          | 有 执行完所以微任务

        |          |

        |___________↓

--------------------↓------------------

                直到栈为空

我们在来看上面的问题

  • 主线程 (宏任务) 打印 1 - 5 - 2 【new Promise 会立即执行,不属于,微任务】

  • 执行所有微任务 promise.then 打印 7

  • 在执行栈中抛出一个【可以执行的 -> 到时间,虽然,第一个 setTimeout 首先注册,在任务队列栈底】宏任务执行 3 - 4

  • 在执行所有微任务 【没有】

  • 在抛出一个宏任务执行 8

  • 在执行所有微任务 【没有】

  • 在抛出一个宏任务执行 6

  • 任务执行完毕

  • 1 5 2 7 3 4 8 6

在谈 NextTick

我去.......,上面这一大堆和 NextTick 有什么关系?

还真有点关系,看源码,我只剪取了主要部分,源码来自 wepy


// 微任务

let microTimerFunc

// 宏任务

let macroTimerFunc

// 默认不用宏任务,因为微任务到优先级高于宏任务

let useMacroTask = false

// 判断 setImmediate 能不能使用

if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {

  // 能使用注册宏任务

  macroTimerFunc = () => {

    setImmediate(flushCallbacks)

  }

  // 是否支持 MessageChannel

} else if (typeof MessageChannel !== 'undefined' && (

  isNative(MessageChannel) ||

  // PhantomJS

  MessageChannel.toString() === '[object MessageChannelConstructor]'

)) {

  // 通过命名通道来通信

  const channel = new MessageChannel()

  const port = channel.port2

  channel.port1.onmessage = flushCallbacks

  macroTimerFunc = () => {

    port.postMessage(1)

  }

} else {

  macroTimerFunc = () => {

    setTimeout(flushCallbacks, 0)

  }

}

// 判断是否可以使用 Promise ie 8 以下不能吧.....

if (typeof Promise !== 'undefined' && isNative(Promise)) {

  const p = Promise.resolve()

  // 注册微任务

  microTimerFunc = () => {

    p.then(flushCallbacks)

  }

} else {

  // 如果不存在,微任务注册微宏任务

  microTimerFunc = macroTimerFunc

}

/**

执行流程就是

宏任务 检测 setImmediate ----- 不能 ----> 降级 MessageChannel ------不能-----> 降级 setTimeout

微任务 Promise ---- 不能 ---> 微任务注册微宏任务

上面的 flushCallbacks 就是你要执行的函数

在 Vue 整个 nextTick 的作用


  // 盗用官方的一个例子

  Vue.component('example', {

  template: '<span>{{ message }}</span>',

  data: function () {

    return {

      message: '未更新'

    }

  },

  methods: {

    updateMessage: function () {

      this.message = '已更新'

      console.log(this.$el.textContent) // => '未更新'

      this.$nextTick(function () {

        console.log(this.$el.textContent) // => '已更新'

      })

    }

  }

})

// 在 updateMessage 方法中,更新数据,立即获取更新后的 dom 是获取不到的,所以得把获取 dom 加到事件队列的栈,异步获取更新后的dom 

主线程更新前 ---> 遇到宏任务或微任务 ---> 放入栈 ---> 主线程执行完成,更新完成 ----> 执行栈 ---- > 获取更新后的dom

总结

相关文章

网友评论

      本文标题:浅析 NextTick

      本文链接:https://www.haomeiwen.com/subject/biqmdctx.html