Vue this.$nextTick原理
虽然 Vue.js 通常鼓励开发人员沿着“数据驱动”的方式思考,避免直接接触 DOM,但是有时我们确实要这么做。比如一个新闻滚动的列表项。如果在这里需要操作dom, 应该是等待 Vue 完成更新 DOM之后。
一、新闻滚动列表
1、在created函数中获取后台数据;
2、模板引擎中用v-for生成列表项;
3、调用滚动函数,假设该滚动函数式原生方法写的;
4、什么时候开始调用滚动函数比较合适呢?
二、this.$nextTick()
官方解释:将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。它跟全局方法 Vue.nextTick 一样,不同的是回调的 this 自动绑定到调用它的实例上。
Vue.component('example', { template: '<span>{{ message }}</span>', data: function () { return { message: 'not updated' } }, methods: { updateMessage: function () { this.message = 'updated' console.log(this.$el.textContent) // => 'not updated' this.$nextTick(function () { console.log(this.$el.textContent) // => 'updated' }) } } })
三、新闻滚动列表中的this.$nextTick()放哪里?
因为数据是根据请求之后获取的,所以应该放到请求的回调函数里面。
四、原理【重点】
前面只是利用一个例子引入话题。
在进行获取数据后,需要对新视图进行下一步操作或者其他操作时,为什么获取不到 DOM呢?
原因:
这里就涉及到 Vue 一个很重要的概念:异步更新队列(JS运行机制 、 事件循环)。
Vue 在观察到数据变化时并不是直接更新 DOM,而是开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。
在缓冲时会去除重复数据,从而避免不必要的计算和DOM操作。
然后,在下一个事件循环 tick 中,Vue 刷新队列并执行实际(已去重的)工作。
所以如果用 for 循环来动态改变数据100次,其实它只会应用最后一次改变,如果没有这种机制,DOM就要重绘100次,是一个很大的开销,损耗性能。
例子:
//改变数据 vm.message = 'changed' //想要立即使用更新后的DOM。这样不行,因为设置message后DOM还没有更新 console.log(vm.$el.textContent) // 并不会得到'changed' //这样可以,nextTick里面的代码会在DOM更新后执行 Vue.nextTick(function(){ console.log(vm.$el.textContent) //可以得到'changed' })
五、常见应用
点击按钮显示原本以 v-show = false 隐藏起来的输入框,并获取焦点。
showsou(){ this.showit = true //修改 v-show document.getElementById("keywords").focus() //在第一个 tick 里,获取不到输入框,自然也获取不到焦点 } //修改 showsou(){ this.showit = true this.$nextTick(function () { // DOM 更新了 document.getElementById("keywords").focus() }) }