前言
this.$nextTick()并不是一个常用的API,但是到了关键的时候还就是必须用。但是它到底是做什么的,很多同学看了文档依然是一头雾水。先看文档:
https://cn.vuejs.org/v2/api/#vm-nextTick
将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。它跟全局方法 Vue.nextTick 一样,不同的是回调的 this 自动绑定到调用它的实例上。
啥意思?下一次更新循环是啥?
Vue的更新循环
先了解一下Vue的异步更新队列。
最直白的说,Vue为了优化性能,使用了一个队列,Vue会先收集所有的数据更新需求,然后压到这个队列里。
比如说你写了几条语句:
this.a = 1
this.b = 2
表面上看,是更新了2个数据,那么视图会变化2次。
但Vue不会让视图变化2次,因为视图变化的开销太大,Vue会在虚拟DOM中把2次变化全部实现,然后一次性渲染。2次数据变化都会压到队列里,Vue一直会等到队列全改完了,才会更新一次视图。这就是一次更新循环。
那么下一次更新循环是啥?
也是最直白的说,一批数据有修改->视图更新一次,这就是一次循环,下一次一批数据有修改->视图更新一次,就是下一次更新循环。
这一次数据修改跟下一次数据修改的分界线是什么?简单但不严谨的说,就是这一波同步任务和下一波同步任务。这其中又涉及到了线程的概念,就是同步语句无论写多少句都是同一波任务,回调函数中的同步语句是另一波任务,有多个回调函数就有多少个下一波任务。
这里面有个问题
Vue有一个节省开销的机制,就是一个节点或组件没有渲染之前,对这个节点的任何修改都是无意义的,比如有一个组件,目前处于v-if=false状态,我想用ref获取这个组件,根本办不到。但问题来了,就是如果你把v-if=false改成true,你依然不能立即获取到。这跟Vue诞生之前的没有虚拟DOM的时代的常识是相悖的。
早期时代,我们动态创建一个节点比如它id是a,只要它插入了DOM,那么可以立即使用document.getElementById('a')获取到它,但这是早期,在Vue里行不通。因为你的“v-if=false改成true”跟获取这个节点或者组件是2次更新循环。
但是,我们写代码的时候,“v-if=false改成true”跟获取这个节点或者组件确实是强相关的操作,我总不能用setTimeout延时后一步操作吧?延时我也不知道延时多久哇?那么怎么办呢?
this.$nextTick()就是干这个用的。
案例一
一个输入框,之前是v-if=false的,现在想显示,且获得焦点,怎么做?
this.isShow = true
this.$nextTick(() => {
this.$refs['它的ref值'].focus()
})
这样就可以了,如果不写this.$nextTick(),会报错说无法给undefined执行.focus操作。
同理,如果你打算获取元素的高度、宽度、位置之类的,也要用this.$nextTick()。
注意:this.$nextTick()的回调函数应当使用箭头函数,以保证this指向vue实例。
案例二
官方针对mounted钩子函数有一句话:
注意 mounted 不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用 vm.$nextTick 替换掉 mounted。
可能之前你看了还是不懂它在说什么,现在就明白了,想对子组件操作,还是得加一个this.$nextTick():
mounted() {
this.$nextTick(() => {
// 你想要对视图进行的修改操作
})
}
没有回调函数会怎样
官方说:
如果没有提供回调且在支持 Promise 的环境中,则返回一个 Promise。请注意 Vue 不自带 Promise 的 polyfill,所以如果你的目标浏览器不是原生支持 Promise (IE:你们都看我干嘛),你得自行 polyfill。
也就是说,如果你在一个Promise链条中写代码写的正Happy,那么你就可以把
this.$nextTick(() => {
// 你想要对视图进行的修改操作
})
改成:
this.$nextTick().then(() => {
// 你想要对视图进行的修改操作
})
这样显得更Promise。
网友评论