Vue.nextTick工作原理以及应用
最近在做仿饿了吗项目时遇到了一个问题,在mounted阶段通过refs获取到HTMLCollection数据时无法读取他的数据,最后也查了很多资料才发现在mounted阶段,mounted 不会承诺所有的子组件也都一起被挂载,所以在此阶段,dom结构还没加载完,js就执行了,最终用vm.nextTick的工作原理。
注:在组件内使用 vm.$nextTick() 实例方法特别方便,因为它不需要全局 Vue,并且回调函数中的 this 将自动绑定到当前的 Vue 实例上:
官网的文档说明
当时我也比较懵逼,什么事更新循环再回调,下面我们就要说说Dom更新的运行机制
异步更新
异步任务是指任务不进入主线程,而直接进入“任务队列”,只有任务队列通知主线程,该任务才会进入主线程,也就是说在数据发生变化时Dom不会立即发生变化,而是按照一定的规则进行更新,关于异步的解析,可以查看阮一峰老师的这篇文章。
具体来说,异步执行的运行机制如下。(同步执行也是如此,因为它可以被视为没有异步任务的异步执行。)
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。
下图就是主线程和任务队列的示意图。
只要主线程空了,就会去读取"任务队列",这就是异步的运行机制。这个过程会不断重复。
event loop
主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环),简单来说数据更改后视图不会立即更新,而是等到同一个事件中所有数据都更新或者事件执行完毕后在统一更新,下面看一个例子
//改变数据
vm.message = 'changed'
//想要立即使用更新后的DOM。这样不行,因为设置message后DOM还没有更新
console.log(vm.$el.textContent) // 并不会得到'changed'
//这样可以,nextTick里面的代码会在DOM更新后执行
Vue.nextTick(function(){
console.log(vm.$el.textContent) //可以得到'changed'
})
下面来一步一步解释
(1)图中第一个步骤即本次更新
1、首先修改数据,这是同步任务。同一事件循环的所有的同步任务都在主线程上执行,形成一个执行栈,此时还未涉及 DOM 。
2、Vue 开启一个异步队列,并缓冲在此事件循环中发生的所有数据改变。如果同一个 watcher 被多次触发,只会被推入到队列中一次。
(2)图中第二个步骤即下次循环
1、同步任务执行完毕,开始执行异步 watcher 队列的任务,更新 DOM 。Vue 在内部尝试对异步队列使用原生的 Promise.then 和 MessageChannel 方法,如果执行环境不支持,会采setTimeout(fn, 0) 代替。
注:DOM至少会在当前tick里面的代码全部执行完毕再更新。所以不可能做到在修改数据后并且DOM更新后再执行,要保证在DOM更新以后再执行某一块代码,就必须把这块代码放到下一次事件循环里面,比如setTimeout(fn, 0),这样DOM更新后,就会立即执行这块代码。
(3)第三个步骤就是下次循环结束后执行
1、 此时通过 Vue.nextTick 获取到改变后的 DOM 通过 setTimeout(fn, 0) 也可以同样获取到。
上一篇: React初探
下一篇: nrm:一个管理npm镜像源的工具