前两天在开发时遇到一个需求:打开对话框的时候自动聚焦其中的输入框。由于原生的 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。但是经过测试,如果对话框默认状态是打开的,就不会报错;明明组件就在那,为什么获取不到呢?

生命周期 update

经过分析,这种现象是由于 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.nextTick

为了解决这个问题,Vue 提供了全局 api Vue.nextTick(),它的作用是提供下次 DOM 更新之后的回调。也就是说,在更新数据后调用 api,就能够获取到重新渲染后的 DOM 并进行相关操作。

nextTick 方法可以广泛适用于各种需要在数据更新后对相关 DOM 进行操作的情景,例如 v-ifwatch 等。

在上文的例子中再加入 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

如果没有提供回调参数,并且浏览器支持 Promise,调用 nextTick 将返回一个 Promise。也就是说下面几种写法是等价的(环境支持的情况下):

Vue.nextTick(function () {...})
Vue.nextTick(() => {...}) Vue.nextTick().then(function () {...})
Vue.nextTick().then(() => {...})

关于 Promise 的介绍和用法,可以参考博文 JS Promise

结语&参考资料

以上是个人对 Vue 中 nextTick api 的一些理解与思考,希望能给你提供帮助。如果有问题或疏漏之处,欢迎在评论中讨论与指正。

我将继续在个人博客中更新自己的学习笔记,以前端技术(Vue框架)为主,感兴趣的话欢迎关注!

参考资料:

Vue 文档 - api

Vue 文档 - 实例

前端 | Vue nextTick 获取更新后的 DOM的更多相关文章

  1. 前端vue 里的tab切换 减少dom操作

    <div class="vuedemo"> <div class="all"> <div class="tabone&q ...

  2. Vue之获取DOM元素与更新DOM后事件的特殊情况

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. Vue.nextTick DOM 更新循环结束之后执行延迟回调

    在下次 DOM 更新循环结束之后执行延迟回调.在修改数据之后立即使用这个方法,获取更新后的 DOM. 简单来说,Vue 在修改数据后,视图不会立刻更新,而是等同一事件循环中的所有数据变化完成之后,再统 ...

  4. vue2.0 正确理解Vue.nextTick()的用途

    什么是Vue.nextTick() 官方文档解释如下: 在下次 DOM 更新循环结束之后执行延迟回调.在修改数据之后立即使用这个方法,获取更新后的 DOM. 获取更新后的DOM,言外之意就是DOM更新 ...

  5. vue nextTick使用

    Vue nextTick使用 vue生命周期 原因是在created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js代码放进Vue. ...

  6. 我理解的关于Vue.nextTick()的正确使用

    什么是Vue.nextTick() 官方文档解释如下: 在下次 DOM 更新循环结束之后执行延迟回调.在修改数据之后立即使用这个方法,获取更新后的 DOM. 我理解的官方文档的这句话的侧重点在最后那半 ...

  7. Vue.nextTick()的正确使用

    Vue异步执行DOM更新.只要观察导数据变化,Vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变,如果同一个watcher被多次触发,只会一次推入到队列中.这种在缓冲时去除重复数据对于避免 ...

  8. Vue系列---理解Vue.nextTick使用及源码分析(五)

    _ 阅读目录 一. 什么是Vue.nextTick()? 二. Vue.nextTick()方法的应用场景有哪些? 2.1 更改数据后,进行节点DOM操作. 2.2 在created生命周期中进行DO ...

  9. vue.nextTick()方法的使用详解

    什么是Vue.nextTick()??   定义:在下次 DOM 更新循环结束之后执行延迟回调.在修改数据之后立即使用这个方法,获取更新后的 DOM. 所以就衍生出了这个获取更新后的DOM的Vue方法 ...

随机推荐

  1. JAVA在JDK1.8中Stream流的使用

    Stream流的map使用 转换大写 List<String> list3 = Arrays.asList("zhangSan", "liSi", ...

  2. 【LeetCode】213. House Robber II 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 题目地址:https://leetcode.com/problems/house-rob ...

  3. 洛谷 P1434 [SHOI2002]滑雪(DP,记忆化搜索)

    题目描述 Michael喜欢滑雪.这并不奇怪,因为滑雪的确很刺激.可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你.Michael想知道在一个区域中最长 ...

  4. 「算法笔记」Min_25 筛

    戳 这里(加了密码).虽然写的可能还算清楚,但还是不公开了吧 QwQ. 真的想看的 私信可能会考虑给密码 qwq.就放个板子: //LOJ 6053 简单的函数 f(p^c)=p xor c #inc ...

  5. AT-GAN: A Generative Attack Model for Adversarial Transferring on Generative Adversarial Nets

    目录 概 主要内容 符号说明 Original Generator Transfer the Generator Wang X., He K., Guo C., Weinberger K., Hopc ...

  6. 跨域The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed.解决方案

    使用Ajax跨域请求资源,Nginx作为代理,出现:The 'Access-Control-Allow-Origin' header contains multiple values '*, *', ...

  7. [opencv]zxing c++ 库的编译,安装,以及api的介绍

    环境:ubuntu 16.04 1. 下载:zxing的源码 git clone https://github.com/15903016222/zxing-cpp.git2. 安装编译依赖的工具:cm ...

  8. CS5212 pin to pin 替代RTD2166|DP转VGA芯片|CS5212转换电路设计方法

    CS5212适用于设计DP转VGA转换电路,主要用在嵌入式单片机基于工业机或者INTEL X86主板上面,也适用于多个电子配件市场和显示器应用程序,如笔记本电脑.主板.台式机.适配器.转换器和转接器. ...

  9. MySQL数据库报错 > 1366 - Incorrect string value: ‘\xE6\xB1\x9F\xE6\x96\x87‘ for column ‘Teacher‘ at row 1

    数据库报错这个多半是数据库在创建的时候没有选择字符编码,导致输入中文的时候出现报错. > 1366 - Incorrect string value: '\xE6\xB1\x9F\xE6\x96 ...

  10. Java,JDK安装及环境配置

    jdk安装及环境配置 一.jdk安装 1.找到jdk安装包 2.安装jdk 3.安装jre 二.环境变量配置          1.JAVA_HOME JAVA_HOME C:\Program Fil ...