We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
关于JavaScript 事件循环的知识可以看阮老师的 JavaScript 运行机制详解:再谈Event Loop
知识回顾: (1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。 (2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。 (3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。 (4)主线程不断重复上面的第三步。
可能你还没有注意到,Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际工作。
例如,当你设置 vm.someData = 'new value',该组件不会立即重新渲染。当刷新队列时,组件会在下一个事件循环“tick”中更新。多数情况我们不需要关心这个过程,但是如果你想基于更新后的 DOM 状态来做点什么,这就可能会有些棘手。虽然 Vue.js 通常鼓励开发人员使用“数据驱动”的方式思考,避免直接接触 DOM,但是有时我们必须要这么做。为了在数据变化之后等待 Vue 完成更新 DOM,可以在数据变化之后立即使用 Vue.nextTick(callback)。这样回调函数将在 DOM 更新完成后被调用。
简单来说, VUE 会把一系列相关的 dom 操作放在一个 tick 中,一起插入任务队列中并等待执行 必须要使用 Vue.nextTick(callback) 才能确保上一轮 tick 中 dom 更新完成
tick
例如:
<div id="example">{{message}}</div>
var vm = new Vue({ el: '#example', data: { message: 'old message' } }) vm.message = 'new message' // 更改数据 console.log(vm.$el.textContent) // old message Vue.nextTick(function () { console.log(vm.$el.textContent) // new message })
在 vue 2.6 版本的源码中,所有的相关逻辑都在 next-tick.js 文件中 一开始声明了 callbacks 数组,里面装着一次 tick 需要执行的所有函数
callbacks
const callbacks = []
vue 通过 nextTick 把需要执行的异步操作 push 到 callbacks 中, 并且执行timerFunc()
timerFunc()
nextTick 实现
export function nextTick (cb?: Function, ctx?: Object) { let _resolve callbacks.push(() => { if (cb) { try { cb.call(ctx) } catch (e) { handleError(e, ctx, 'nextTick') } } else if (_resolve) { _resolve(ctx) } }) if (!pending) { pending = true timerFunc() } // $flow-disable-line if (!cb && typeof Promise !== 'undefined') { return new Promise(resolve => { _resolve = resolve }) } }
timerFunc 实现
timerFunc() 就是根据浏览器的兼容性声明的异步函数 Vue 在会尝试使用原生的 Promise.then、MutationObserver 和 setImmediate,如果执行环境不支持,则会采用 setTimeout(fn, 0) 代替。
const p = Promise.resolve() timerFunc = () => { p.then(flushCallbacks) }
let counter = 1 const observer = new MutationObserver(flushCallbacks) const textNode = document.createTextNode(String(counter)) observer.observe(textNode, { characterData: true }) timerFunc = () => { counter = (counter + 1) % 2 textNode.data = String(counter) } isUsingMicroTask = true
setImmediate
timerFunc = () => { setImmediate(flushCallbacks) }
timerFunc = () => { setTimeout(flushCallbacks, 0) }
可见 timerFunc 本质上就是把 flushCallbacks 加入异步队列中 而 flushCallbacks 就是 依次执行 callbacks 里的回调并清空该数组
timerFunc
flushCallbacks
flushCallbacks 实现
function flushCallbacks () { pending = false const copies = callbacks.slice(0) callbacks.length = 0 for (let i = 0; i < copies.length; i++) { copies[i]() } }
到此,一轮 tick 就执行完成了
1、h5 规范中 event loop 中的任务分为(宏)任务: (marco) task 和 微任务: microtask 如果浏览器原生支持 Promise,基于 timerFunc 的实现,tick 中的任务属于 microtask。 2、Vue.js 提供了 2 种调用 nextTick 的方式,一种是全局 API Vue.nextTick,一种是实例上的方法 vm.$nextTick,无论我们使用哪一种,最后都是调用 next-tick.js 中实现的 nextTick 方法。 3、本质上 vue 中所有的 dom 渲染都是通过 nextTick 执行的,也就是说 vue 中所有的 dom 渲染都是异步执行
VUE 文档
The text was updated successfully, but these errors were encountered:
No branches or pull requests
JavaScript 事件循环
关于JavaScript 事件循环的知识可以看阮老师的 JavaScript 运行机制详解:再谈Event Loop
知识回顾:

(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。
VUE 异步更新队列
可能你还没有注意到,Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际工作。
例如,当你设置 vm.someData = 'new value',该组件不会立即重新渲染。当刷新队列时,组件会在下一个事件循环“tick”中更新。多数情况我们不需要关心这个过程,但是如果你想基于更新后的 DOM 状态来做点什么,这就可能会有些棘手。虽然 Vue.js 通常鼓励开发人员使用“数据驱动”的方式思考,避免直接接触 DOM,但是有时我们必须要这么做。为了在数据变化之后等待 Vue 完成更新 DOM,可以在数据变化之后立即使用 Vue.nextTick(callback)。这样回调函数将在 DOM 更新完成后被调用。
简单来说, VUE 会把一系列相关的 dom 操作放在一个
tick
中,一起插入任务队列中并等待执行必须要使用 Vue.nextTick(callback) 才能确保上一轮
tick
中 dom 更新完成例如:
vue nextTick 实现源码
在 vue 2.6 版本的源码中,所有的相关逻辑都在 next-tick.js 文件中
一开始声明了
callbacks
数组,里面装着一次tick
需要执行的所有函数vue 通过 nextTick 把需要执行的异步操作 push 到
callbacks
中, 并且执行timerFunc()
nextTick 实现
timerFunc 实现
😜这个我不懂
setImmediate
可见
timerFunc
本质上就是把flushCallbacks
加入异步队列中而
flushCallbacks
就是 依次执行callbacks
里的回调并清空该数组flushCallbacks 实现
到此,一轮
tick
就执行完成了总结
1、h5 规范中 event loop 中的任务分为(宏)任务: (marco) task 和 微任务: microtask
如果浏览器原生支持 Promise,基于
timerFunc
的实现,tick
中的任务属于 microtask。2、Vue.js 提供了 2 种调用 nextTick 的方式,一种是全局 API Vue.nextTick,一种是实例上的方法 vm.$nextTick,无论我们使用哪一种,最后都是调用 next-tick.js 中实现的 nextTick 方法。
3、本质上 vue 中所有的 dom 渲染都是通过 nextTick 执行的,也就是说 vue 中所有的 dom 渲染都是异步执行
参考资料
VUE 文档
The text was updated successfully, but these errors were encountered: