this.$nextTick()这个方法的使用频率还是比较高的,但是之前也只是在用,不是很清楚其中的原理,今天就来探究一下其中的原理以及什么场景中适合使用。
官网定义
将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。它跟全局方法 Vue.nextTick 一样,不同的是回调的 this 自动绑定到调用它的实例上。
这段话中比较难以理解的地方就是“下次DOM更新循环之后”,那么它到底是什么意思呢,其实Vue实现响应式并不是数据发生变化后DOM立即随着改变,而是异步地执行DOM更新。让我们再来深入了解一下异步模式。
什么是异步模式
异步模式的由来:
JavaScript是一个单线程的语言,所有的任务都在一个线程上面完成,但是如果遇到耗时的任务(比如ajax请求),线程就会出现等待,导致页面“假死”,因为JavaScript停不下来,也就无法响应用户的行为。这里用一个图示就很好理解了。
上图绿色部分就是程序运行的时间,红色部分是程序等待的时间,可以看出等待时间太长,会导致程序运行缓慢,影响用户体验。
为了解决单线程的这种等待问题,Event Loop线程的概念就应运而生了,简单来说,就是在程序中设置两个线程:一个负责程序本身的运行,称为"主线程";另一个负责主线程与其他进程(主要是各种I/O操作)的通信,被称为"Event Loop线程"(可以译为"消息线程")。
上图主线程的绿色部分,还是表示运行时间,而橙色部分表示空闲时间。每当遇到I/O的时候,主线程就让Event Loop线程去通知相应的I/O程序,然后接着往后运行,所以不存在红色的等待时间。等到I/O程序完成操作,Event Loop线程再把结果返回主线程。主线程就调用事先设定的回调函数,完成整个任务。
可以看到,由于多出了橙色的空闲时间,所以主线程得以运行更多的任务,这就提高了效率。这种运行方式就称为"异步模式"。
DOM更新过程
我们已经了解了,JavaScript是通过Event Loop线程的方式来执行异步操作的,那么异步操作更新DOM的过程是怎么执行的呢。
1.修改 Vue 中的 Data 时,就会触发所有和这个 Data 相关的 Watcher 。
2.将所有的 Watcher 加入异步队列,然后执行异步任务。
3.在异步任务的回调中,对 Watcher 进行排序,然后执行对应的 DOM 更新
再详细解释一下就是:当一个data更新时,会触发data.set()方法,然后调用dep.notify(),dep会遍历所有与该data相关的watcher执行update()方法,JavaScript将所有的watcher添加到异步队列,然后执行异步队列更新DOM。
到这里我们大概就清楚了“下次DOM更新循环之后”是什么意思了,简单来说就是在DOM更新完成之后,this.$nextTick()的回调函数里就能拿到更新后的DOM节点上的值了。
于是我们可以推算出什么时候需要用this.$nextTick()方法了。
this.$nextTick使用场景
1,在Vue生命周期的created()钩子函数中进行的DOM操作。 因为created()执行的时候DOM还未进行渲染,此时操作DOM是没有用的,所以在这个阶段进行任何DOM操作都要放在this.$nextTick()的回调函数中才会生效。
2,在修改了某个数据后,想要获取对应DOM上的值时。举个简单的例子:
<div ref="hello">{{value}}</div>
......
data() {
return {
value: "hello"
}
},
methods: {
getVal(){
this.value = "你好";
console.log(this.$refs['hello'].innerText); //hello
this.$nextTick(()=>{
console.log(this.$refs['hello'].innerText); //你好
})
}
}
当执行getVal()函数的时候,对value进行了修改,这时候DOM还没有立即更新,如果直接获取DOM节点上的value,仍然是修改前的value。而如果从this.$nextTick()的回调函数中获取DOM节点上的value,就能够得到修改后的value了。