前两天在开发时遇到一个需求:打开对话框的时候自动聚焦其中的输入框。由于原生的 autofocus 属性不起作用,需要使用组件库提供的 focus 方法手动手动获取焦点。于是有如下代码:
<el-button @click="openDialog">点击打开 Dialog</el-button>
<el-dialog :visible.sync="dialogVisible">
<el-input v-model="input" ref="input"></el-input>
</el-dialog>
methods: {
openDialog() {
this.dialogVisible = true;
const input = this.$refs.input;
input.focus();
},
},
结果报错了,原因是没有获取到 input 组件;通过 log,也验证了 this.$refs.input 的值确实是 undefined。但是经过测试,如果对话框默认状态是打开的,就不会报错;明明组件就在那,为什么获取不到呢?
经过分析,这种现象是由于 vue 实例的更新机制造成的。从下方的生命周期图(局部)中可以看出,组件装载好之后,遇到数据变化时将重新渲染虚拟 dom(可以理解为 html 中的组件节点)。在本例中,隐藏的 Dialog 组件(以及其中的 input 组件)本来并没有渲染在 DOM 中,是在观察到 dialogVisible 属性变为 true 后再进行更新渲染的。
而网页渲染通常是一个异步任务,因此在 visible 属性刚刚更改时(一个函数中是同步过程),DOM 渲染还没有进行,因此自然获取不到此时还不存在的 input 组件了。
关于异步、JS任务队列、宏任务与微任务等概念的更多介绍,可参考博文JS多线程:任务队列
为了更直观地展示这个过程,可以在更新前后的钩子函数中试图获取组件并进行打印:
beforeUpdate() {
console.log("beforeUpdate");
const input = this.$refs.input;
console.log(input);
},
updated() {
console.log("updated");
const input = this.$refs.input;
console.log(input);
},
methods: {
openDialog() {
this.dialogVisible = true;
console.log("click open");
},
},
结果如下,可以验证之前的分析和猜想:
click open
beforeUpdate
undefined
updated
VueComponent {...}
为了解决这个问题,Vue 提供了全局 api Vue.nextTick(),它的作用是提供下次 DOM 更新之后的回调。也就是说,在更新数据后调用 api,就能够获取到重新渲染后的 DOM 并进行相关操作。
nextTick 方法可以广泛适用于各种需要在数据更新后对相关 DOM 进行操作的情景,例如 v-if 、watch 等。
在上文的例子中再加入 nextTick:
openDialog() {
this.dialogVisible = true;
console.log("click open");
this.$nextTick(function () {
console.log("next tick");
const input = this.$refs.input;
console.log(input);
input.focus();
});
},
可以看到,回调确实是在 DOM 更新之后,也就是 updated 执行之后才执行的。获取组件与手动获得焦点的操作也能够正确执行了。
click open
beforeUpdate
undefined
updated
VueComponent {...}
next tick
VueComponent {...}
如果没有提供回调参数,并且浏览器支持 Promise,调用 nextTick 将返回一个 Promise。也就是说下面几种写法是等价的(环境支持的情况下):
Vue.nextTick(function () {...})
Vue.nextTick(() => {...})
Vue.nextTick().then(function () {...})
Vue.nextTick().then(() => {...})
来自:https://www.cnblogs.com/skuld-yi/archive/2022/01/24/15838944.html
Vue中有个API是nextTick,官方文档是这样介绍作用的:将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。理解这部分内容,有助于理解Vue对页面的渲染过程
异步更新机制使用微任务或宏任务,基于事件循环运行,在 Vue 中对性能起着至关重要的作用,它对重复冗余的 watcher 进行过滤。而 nextTick 根据不同的环境,使用优先级最高的异步任务
定义: 在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM,看完是不是有一堆问号?我们从中找出来产生问号的关键词
vue是异步渲染的,当data改变之后,DOM不会立刻被渲染,页面渲染时会将data的修改做整合,多次data修改只会做整合最后一次性渲染出来,这也是异步渲染的原因。只有异步渲染才可以实现整合操作
在Vue使用组件时,对于组件数据的更改不会立即反应在DOM中,因为Vue是异步更新DOM的。所以需要使用nextTick()或者$nextTick()函数来更新DOM。
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!