官方给的 Vue.nextTick 用法
关于Vue.nextTick 的疑惑
- Vue.nextTick 是用来干嘛的
- Vue.nextTick 是在什么时候用
- Vue.nextTick 的原理是怎么样的
解释上面的问题
- Vue.nextTick是用来更新vue的视图的。
- Vue.nextTick在你的视图已经渲染了,需要再一次更新视图
- Vue.nextTick 的原理分成两个 1,异步 2,事件循环
- 1.异步解释
1.vue 视图更新解释
Vue 实现响应式并不是你想的那种,数据更新,视图就对应的更新了(dom)。而是按照vue自己制定的一些策略来进行视图(dom)更新
2.javascript 的运行机制问题理解 异步问题和同步问题
2.1.javascript 的特点
js 是单线程的运行 (具体单线程是什么自己百度一下) 单线程的机制就是说
你在同一时间只能做一件事
2.2 js的同步和异步问题差异
同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
异步任务:不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
具体来说异步运行机制:(下面一段百度复制的)
(1) 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。
只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制。这个过程会不断重复。
任务队列是一个事件队列,主线程读取"任务队列",就是读取里面有哪些事件。
“任务队列"中的事件,除了IO设备的事件以外,还包括一些用户产生的事件(比如鼠标点击、页面滚动等等)。只要指定过回调函数,这些事件发生时就会进入"任务队列”,等待主线程读取。
回调函数
所谓"回调函数"(callback),就是那些会被主线程挂起来的代码。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。
- 事件循环
事件循环如字面意识就是循环执行事件(这里的事件是指调用任务队列的事件)
举例说明:一条流水线上(一个人) 同步任务就如同一件件物品,但是有的物品不合格或者其他原因,一个人处理不过来,第一次流水线循环的时候,这个不合格的物品因为没有时间执行,所以那个小本本记下来,等流水线上的执行完了,再回来看小本本上的那些事件(执行)
官方说法是这个样的:
事件循环:JS 会创建一个类似于 while (true) 的循环,每执行一次循环体的过程称之为 Tick。每次 Tick 的过程就是查看是否有待处理事件,如果有则取出相关事件及回调函数放入执行栈中由主线程执行。待处理的事件会存储在一个任务队列中,也就是每次 Tick 会查看任务队列中是否有需要执行的任务。
- 主线程和任务队列的示意图(这张图是抄的不是自己画的)
Vue是怎么实现的这个功能的呢Vue.nextTick()
- 你在修改数据的时候就相当于一个同步任务(你不要说为什么修改数据是一个同步任务,而不是异步任务,这是相对的,你修改数据到视图(dom)更新是两件事,你又想两件事一起执行这是不可能的(js是单线程)所以你要去更新视图的时候就要开启一个异步任务了)
- Vue 开启一个异步队列,并缓冲在此事件循环中发生的所有数据改变。如果同一个 watcher 被多次触发,只会被推入到队列中一次。
- 同步任务执行完毕,开始执行异步 watcher 队列的任务,更新 DOM 。Vue 在内部尝试对异步队列使用原生的 Promise.then 和 MessageChannel 方法,如果执行环境不支持,会采用 setTimeout(fn, 0) 代替。
- 此时通过 Vue.nextTick 获取到改变后的 DOM 。通过 setTimeout(fn, 0) 也可以同样获取到
- 简单总结事件循环:
同步代码执行 -> 查找异步队列,推入执行栈,执行Vue.nextTick[事件循环1] ->查找异步队列,推入执行栈,执行Vue.nextTick[事件循环2]…
应用场景
- 获取修改后的值
<template>
<div class="hello">
<div>
<button id="firstBtn" @click="testClick()" ref="aa">{{testMsg}}</button>
</div>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
testMsg:"原始值",
}
},
methods:{
testClick:function(){
let that=this;
that.testMsg="修改后的值";
console.log(that.$refs.aa.innerText); //that.$refs.aa获取指定DOM,输出:原始值
}
}
}
</script>
使用this.$nextTick
methods:{
testClick:function(){
let that=this;
that.testMsg="修改后的值";
that.$nextTick(function(){
console.log(that.$refs.aa.innerText); //输出:修改后的值
});
}
}
- 当项目中你想在改变DOM元素的数据后基于新的dom做点什么,对新DOM一系列的js操作都需要放进Vue.nextTick()的回调函数中;通俗的理解是:更改数据后当你想立即使用js操作新的视图的时候需要使用它
<template>
<div class="hello">
<h3 id="h">{{testMsg}}</h3>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
testMsg:"原始值",
}
},
methods:{
changeTxt:function(){
let that=this;
that.testMsg="修改后的文本值"; //vue数据改变,改变dom结构
let domTxt=document.getElementById('h').innerText; //后续js对dom的操作
console.log(domTxt); //输出可以看到vue数据修改后DOM并没有立即更新,后续的dom都不是最新的
if(domTxt==="原始值"){
console.log("文本data被修改后dom内容没立即更新");
}else {
console.log("文本data被修改后dom内容被马上更新了");
}
},
}
}
</script>
使用Vue.nextTick后
changeTxt:function(){
let that=this;
that.testMsg="修改后的文本值"; //修改dom结构
that.$nextTick(function(){ //使用vue.$nextTick()方法可以dom数据更新后延迟执行
let domTxt=document.getElementById('h').innerText;
console.log(domTxt); //输出可以看到vue数据修改后并没有DOM没有立即更新,
if(domTxt==="原始值"){
console.log("文本data被修改后dom内容没立即更新");
}else {
console.log("文本data被修改后dom内容被马上更新了");
}
});
},