网站首页 > 技术文章 正文
最近在面试的时候被面试官问到,如何在vue中获取到最新的DOM元素,瞬间内心狂喜,自信地对面试官说不就是用nextTick嘛,当DOM元素更新时会执行传入nextTick的回调函数,我们在回调函数中就可以获取到最新的DOM了。
嗯,没错,那你手写一个nextTick吧。
痛苦面具,瞬间小脑萎缩了。
什么是nextTick
在 Vue 的生命周期中,有一段时间是异步的,有时候会遇到数据还未挂载到DOM节点上就试图获取该数据那么此时我们获取到的数据并不是最新的,而 nextTick 就是让我们在这段异步时间结束后执行自己的代码的工具。它确保在DOM更新后执行回调 (起到了等待DOM渲染的作用)。
nextTick的用法
<template>
<div>
<button @click="add()">添加</button>
<ul>
<li v-for="(i, index) in list" ref="l" key="index">{{ i }}</li>
</ul>
</div>
</template>
<script setup>
import { nextTick, ref } from "vue";
const l = ref(null)
const list = ref([1, 2, 3])
console.log(l.value);
</script>
大家猜猜这段代码的运行结果是什么?
请看下图:
为什么最后会输出null呢?
这就是一个很经典的问题,在dom节点还未挂载时我们打印该元素,所以打印的结果为null。
我们可以看vue的官方文档给出的生命周期示意图,很好的解释了这一问题。
在这段代码中,生命周期setup是最先执行的,所以在dom节点还未挂载前就会执行console.log(l.value);打印出null。
那么我们怎么在dom节点挂载后再打印呢? 接下来将要请出本文的主角nextTick了
运行下面这段代码
<template>
<div>
<button @click="add()">添加</button>
<ul>
<li v-for="(i, index) in list" ref="l" key="index">{{ i }}</li>
</ul>
</div>
</template>
<script setup>
import { nextTick, ref } from "vue";
const l = ref(null)
const list = ref([1, 2, 3])
nextTick(() => {
console.log(l.value.length);
})
</script>
运行这段代码,我们可以看到,nextTick等待DOM渲染完毕之后再执行回调函数,这样就完美的解决了问题。
如何手写nextTick
搞懂了上面的这些内容,手写一个nextTick对于大家来说应该可以秒了,接下来让我们一起来看看怎么手写nextTick吧。
根据上面的代码我们可以知道,nextTick函数在DOM节点发生变化时,会执行传入的回调函数。现在的问题就是我们怎么来监听DOM节点的变化呢?
我们都知道在vue中所有的组件最终都是经过编译然后挂载到index.html中的id为app的容器上,所以我们只需要监听该id为app的容器就能够实现对DOM节点更新的监视。
好了,有了以上知识的铺垫,直接开戳
<template>
<div>
<button @click="add()">添加</button>
<ul>
<li v-for="(i, index) in list" ref="l" key="index">{{ i }}</li>
</ul>
</div>
</template>
<script setup>
import { nextTick, ref } from "vue";
const l = ref(null)
const list = ref([1, 2, 3])
nextTick(() => {
console.log(l.value.length);
})
</script>
在这段代码中,用了原生js的MutationObserver方法,该方法可以用于观察DOM树的变化,在该代码中用实例化了一个观察者对象并指定观察的容器与触发回调函数的条件。
监听 DOM 元素变化的原理:
- MutationObserver 被用来观察 DOM 树的变化。
- 当 add 方法被调用时,list 的变化会导致列表项的增加,进而引起 DOM 树的变化。
- MutationObserver 会检测到这些变化,并在变化发生后调用回调函数,从而执行传入的 fn 函数。
好了,接下来我们来试一下自己手搓的nextTick函数的效果。
可以看到当点击添加按钮时,可以实时获取到当前列表的长度,完美解决。
写到这里,大家觉得这种方法对吗?显然有问题,用MutationObserver方法来监听DOM树的变化时当往根节点中添加其他的元素时也会触发回调函数,所以我们要用事件循环机制来解决。
使用了原生 JavaScript 的 Promise 和 setTimeout。这个实现方式模拟了 Vue.js 的 nextTick 行为,并且可以在浏览器环境中运行。
实现 nextTick 的基本思路:
- 异步执行:确保回调是在当前任务完成之后执行的。
- 队列管理:如果多次调用 nextTick,确保它们按照顺序执行,而不是并发执行。
- 立即执行:如果可能的话,在微任务队列中立即执行(利用 Promise)。
<template>
<div>
<button @click="add()">添加</button>
<ul>
<li v-for="(i, index) in list" ref="l" :key="index">{{ i }}</li>
</ul>
</div>
</template>
<script setup>
import { nextTick, ref } from "vue";
const list = ref([1, 2, 3])
const l = ref(null);
console.log(l.value);
const add = () => {
list.value.push(list.value.length + 1);
myNextTick(() => {
console.log(l.value.length);
});
};
function myNextTick(callback) {
return new Promise(resolve => {
setTimeout(() => {
if (callback) {
callback()
}
resolve()
}, 0)
})
}
</script>
当尝试更新列表长度时,正常获取到最新的dom结构。
以上仅为简单模拟过程,详细过程请参考Vue源码。
总结
本篇文章就到此结束了,希望大家以后再碰到手写nextTick的时候能顺利秒杀
- 上一篇: 分布式控制系统DCS和SCADA的区别和联系
- 下一篇: 告别轮询!SSE 如何实现前端高效实时通信?
猜你喜欢
- 2025-06-30 分布式控制系统DCS和SCADA的区别和联系
- 2025-06-30 Redis系列:深刻理解高性能Redis的本质
- 2025-06-30 数字孪生与物联网(IoT)集成:设备实时监控与预警的联动方案
- 2024-10-07 语音直播系统源码,前端监控存在的意义
- 2024-10-07 为什么大厂前端监控都在用GIF做埋点?
- 2024-10-07 前端错误监控-Sentry自动捕获前端应用异常原理
- 2024-10-07 「JVM进阶之路」:性能监控工具-命令行篇
- 2024-10-07 智能一体化视频监控前端箱的技术参数
- 2024-10-07 前端监控 SDK 的一些技术要点原理分析
- 2024-10-07 前端监控 SDK 开发分享 前端监控 sdk 开发分享方案
你 发表评论:
欢迎- 580℃几个Oracle空值处理函数 oracle处理null值的函数
- 575℃Oracle分析函数之Lag和Lead()使用
- 561℃Oracle数据库的单、多行函数 oracle执行多个sql语句
- 560℃0497-如何将Kerberos的CDH6.1从Oracle JDK 1.8迁移至OpenJDK 1.8
- 556℃Oracle 12c PDB迁移(一) oracle迁移到oceanbase
- 545℃【数据统计分析】详解Oracle分组函数之CUBE
- 533℃最佳实践 | 提效 47 倍,制造业生产 Oracle 迁移替换
- 529℃Oracle有哪些常见的函数? oracle中常用的函数
- 最近发表
- 标签列表
-
- 前端设计模式 (75)
- 前端性能优化 (51)
- 前端模板 (66)
- 前端跨域 (52)
- 前端缓存 (63)
- 前端react (48)
- 前端aes加密 (58)
- 前端脚手架 (56)
- 前端md5加密 (54)
- 前端路由 (61)
- 前端数组 (73)
- 前端js面试题 (50)
- 前端定时器 (59)
- 前端懒加载 (49)
- Oracle RAC (73)
- oracle恢复 (76)
- oracle 删除表 (48)
- oracle 用户名 (74)
- oracle 工具 (55)
- oracle 内存 (50)
- oracle 导出表 (57)
- oracle 中文 (51)
- oracle的函数 (57)
- 前端调试 (52)
- 前端登录页面 (48)
本文暂时没有评论,来添加一个吧(●'◡'●)