新闻资讯
vue为什么要异步更新视图
为什么要异步更新视图
看下面的代码:
<template>
<div>
<div>{{test}}</div>
</div>
</template>
export default { data () { return { test: 0
};
}, mounted () { for(let i = 0; i < 1000; i++) {
this.test++;
}
}
}
现在有这样的一种情况,mounted的时候test的值会被++循环执行1000次。 每次++时,都会根据响应式触发setter->Dep->Watcher->update->run。 如果这时候没有异步更新视图,那么每次++都会直接操作DOM更新视图,这是非常消耗性能的。 所以Vue实现了一个queue队列,在下一个Tick(或者是当前Tick的微任务阶段)的时候会统一执行queue中Watcher的run。同时,拥有相同id的Watcher不会被重复加入到该queue中去,所以不会执行1000次Watcher的run。最终更新视图只会直接将test对应的DOM的0变成1000。 保证更新视图操作DOM的动作是在当前栈执行完以后下一个Tick(或者是当前Tick的微任务阶段)的时候调用,大大优化了性能。
应用场景
在操作DOM节点无效的时候,就要考虑操作的实际DOM节点是否存在,或者相应的DOM是否被更新完毕。
比如说,在created钩子中涉及DOM节点的操作肯定是无效的,因为此时还没有完成相关DOM的挂载。解决的方法就是在nextTick函数中去处理DOM,这样才能保证DOM被成功挂载而有效操作。
还有就是在数据变化之后要执行某个操作,而这个操作需要使用随数据改变而改变的DOM时,这个操作应该放进Vue.nextTick。
之前在做慕课网音乐webApp的时候关于播放器内核的开发就涉及到了这个问题。下面我把问题简化:
现在我们要实现一个需求是点击按钮变换audio标签的src属性来实现切换歌曲。
<div id="example">
<audio ref="audio" :src="url"></audio>
<span ref="url"></span>
<button @click="changeUrl">click me</button>
</div>
const musicList = [ 'http://sc1.111ttt.cn:8282/2017/1/11m/11/304112003137.m4a?tflag=1519095601&pin=6cd414115fdb9a950d827487b16b5f97#.mp3', 'http://sc1.111ttt.cn:8282/2017/1/11m/11/304112002493.m4a?tflag=1519095601&pin=6cd414115fdb9a950d827487b16b5f97#.mp3', 'http://sc1.111ttt.cn:8282/2017/1/11m/11/304112004168.m4a?tflag=1519095601&pin=6cd414115fdb9a950d827487b16b5f97#.mp3' ];
var vm = new Vue({
el: '#example',
data: {
index: 0,
url: '' },
methods: { changeUrl() {
this.index = (this.index + 1) % musicList.length
this.url = musicList[this.index];
this.$refs.audio.play();
}
}
});
毫无悬念,这样肯定是会报错的:
Uncaught (in promise) DOMException: The element has no supported sources.
原因就在于audio.play()是同步的,而这个时候DOM更新是异步的,src属性还没有被更新,结果播放的时候src属性为空,就报错了。
解决办法就是在play的操作加上this.$nextTick()。
this.$nextTick(function() {
this.$refs.audio.play();
});
原文链接:https://juejin.im/post/5e53f07d51882549036940fc
回复列表