0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

解析$nextTick魔力,为啥大家都爱它?

京东云 来源:京东保险 卓雅倩 作者:京东保险 卓雅倩 2024-12-17 10:02 次阅读

作者:京东保险 卓雅倩

1.为什么需要使用$nextTick?

首先我们来看看官方对于$nextTick的定义:

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

由于vue的试图渲染是异步的,生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中,原因是在created()钩子函数执行的时候DOM其实并未进行渲染,而此时进行DOM操作是徒劳的,所以一定要将DOM操作的js代码放到Vue.nextTick()的回调函数中。除了在created()钩子函数中使用之外咱们还会遇到很多种需要使用到Vue.nextTick()的场景,如下所示:

咱们日常生活中常常会遇上上述场景,会有很多问题,就比如当我们点击按钮更新数据时候,如下代码示例:

< template >

< input type="text" v-if = "isShow" ref="input"/ >点击显示输入框,并且获取输入框焦点< /button >

< /template > < script > export default { data() { return { isShow: false } }, methods : { handleClick () { this.isShow = true this.$refs.input.focus() //控制栏会报错,因为还没有这个dom } } } < /script >

点击控制栏显示效果:控制栏报错,提示没有获取到dom元素;

wKgZPGdg27GAAbCXAAH_Vfnf58U128.png

所以现在Vue.nextTick()派上了用场,Vue.nextTick() 方法的作用正是等待上一次事件循环执行完毕,并在下一次事件循环开始时再执行回调函数。这样可以保证回调函数中的 DOM 操作已经被 Vue.js 进行过更新,从而避免了一些潜在的问题,如下代码所示:

< template >

< input type="text" v-if = "isShow" ref="input"/ >点击显示输入框,并且获取输入框焦点< /button >

< /template > < script > export default { data() { return { isShow: false } }, methods : { handleClick () { this.isShow = true this.$nextTick(()=>{ this.$refs.input.focus() }) } } } < /script >

加上this.$nextTick后就能够使得输入框获取到焦点;

总而言之Vue.nextTick()就是下次 DOM 更新渲染后执行延迟回调函数。在日常开发中,我们在修改数据之后使用这个方法,就可以获取更新后的 DOM的同时进行在对DOM进行相对应操作的 js代码;

2.$nextTick如何实现的?

JS是单线程执行的,所有的同步任务都是在主线程上执行的,形成了一个执行栈,从上到下依次执行,异步代码会放在任务队列里面。

•同步任务

在主线程里执行,当浏览器第一遍过滤html文件的时候可以执行完;(在当前作用域直接执行的所有内容,包括执行的方法、new出来的对象)

•异步任务

耗费时间较长或者性能较差的,浏览器执行到这些的时候会将其丢到异步任务队列中,不会立即执行

同时异步任务分为宏任务(如setTimeout、setInterval、postMessage、setImmediate等)和微任务(Promise、process.nextTick等),浏览器执行这两种任务的优先级不同;会优先执行微任务队列的代码,微任务队列清空之后再执行宏任务的队列,这样循环往复;

JS自上向下进行代码的编译执行,遇到同步代码压入JS执行栈执行后出栈,遇到异步代码放入任务队列,当JS执行栈清空,去执行异步队列中的回调函数,先去执行微任务队列,当微任务队列清空后,去检测执行宏任务队列中的回调函数,直至所有栈和队列清空

整体流程如下图所示:

wKgZO2dg27KAOPNyAASDxoSgV6Q704.png

接下来让我们看看nextTick的源码~

vue将nextTick的源码放在了vue/core/util/next-tick.js中。如下图所示:

wKgZPGdg27OAKML3AAaeel8WjTw493.png

我们把这个文件拆成三个部分来看:

1.nextTick定义函数

我们将nextTick函数单独拿出来,callbacks是一个回调队列,其实调用nextTick就是往这个数组里面传执行任务,callbacks新增回调函数之后执行timerFunc函数,pending是用来限制同一个事件循环内只能执行一次的pending锁;

const callbacks = [] // 回调队列 let pending = false // export function nextTick (cb?: Function, ctx?: Object) { let _resolve callbacks.push(() => { // cb 回调函数会经统一处理压入 callbacks 数组 if (cb) { try { cb.call(ctx) } catch (e) { handleError(e, ctx, 'nextTick') } } else if (_resolve) { _resolve(ctx) } }) // 执行异步延迟函数 timerFunc if (!pending) { pending = true timerFunc() } // $flow-disable-line // 当 nextTick 没有传入函数参数的时候,返回一个 Promise 化的调用 if (!cb && typeof Promise !== 'undefined') { return new Promise(resolve => { _resolve = resolve }) } }

2.timerFunc函数做了四个判断,先后尝试当前环境是否能够使用原生的Promise.then、MutationObserver和setImmediate,不断的降级处理,如果以上三个都不支持,则最后就会直接使用setTimeOut,主要操作就是将flushCallbacks中的函数放入微任务或者宏任务,等待下一个事件循环开始执行;宏任务耗费的时间是大于微任务的,所以在浏览器支持的情况下,优先使用微任务。如果浏览器不支持微任务,使用宏任务;但是,各种宏任务之间也有效率的不同,需要根据浏览器的支持情况,使用不同的宏任务;

export let isUsingMicroTask = false let timerFunc if (typeof Promise !== 'undefined' && isNative(Promise)) { //是否支持Promise const p = Promise.resolve() timerFunc = () => { p.then(flushCallbacks) if (isIOS) setTimeout(noop) } isUsingMicroTask = true } else if (!isIE && typeof MutationObserver !== 'undefined' && ( isNative(MutationObserver) || MutationObserver.toString() === '[object MutationObserverConstructor]' )) { //是否支持MutationObserver let counter = 1 const observer = new MutationObserver(flushCallbacks) const textNode = document.createTextNode(String(counter)) observer.observe(textNode, { characterData: true }) timerFunc = () => { counter = (counter + 1) % 2 textNode.data = String(counter) } isUsingMicroTask = true } else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { timerFunc = () => { //是否支持setImmediate setImmediate(flushCallbacks) } } else { // Fallback to setTimeout. timerFunc = () => { //上面都不行,直接使用setTimeout setTimeout(flushCallbacks, 0) } }

3.flushCallbacks函数

flushCallbacks函数只有几行,也很好理解,将pending锁置为false,同时将callbacks数组复制一份之后再将callbacks置为空,接下来将复制出来的callbacks数组的每个函数依次进行执行,简单来说它的主要作用就是用来执行callbacks中的回调函数;

function flushCallbacks () { pending = false const copies = callbacks.slice(0) callbacks.length = 0 for (let i = 0; i < copies.length; i++) { copies[i]() } }

值得注意的是,$nextTick 并不是一个真正意义上的微任务microtask,而是利用了事件循环机制来实现异步更新。因此,它的执行时机相对于微任务可能会有所延迟,但仍能保证在 DOM 更新后尽快执行回调函数。

总的来说,nextTick就是:

1.将传入的回调函数放入callbacks数组等待执行,定义pending判断锁保证一个事件循环中只能调用一次timerFunc函数;

2.根据环境判断使用异步方式,调用timerFunc函数调用flushCallbacks函数依次执行callbacks中的回调函数;

3.个人小结

nextTick可避免数据更新后导致DOM的数据不一致的问题,提供了更稳定的异步更新机制,解决了created钩子函数DOM未渲染会造成的异步数据渲染问题,但如果过多的使用nextTick会导致事件循环中任务数量和回调函数增多,有可能出现可怕的回调地狱,导致性能下降,同时过度依赖nextTick也会降低代码的可读性,所以大家还是"按需加载"的好~

审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • DOM
    DOM
    +关注

    关注

    0

    文章

    18

    浏览量

    9613
  • vue
    vue
    +关注

    关注

    0

    文章

    58

    浏览量

    7923
收藏 人收藏

    相关推荐

    AMOLED屏有什么魔力?华为、小米都爱

    越来越多的手机厂商在屏幕选择上偏爱OLED,华为、OPPO、vivo、小米等都加入了AMOLED的阵营,这块屏幕究竟存在什么样的魔力,能够吸引这么多厂商?
    发表于 08-24 09:45 3016次阅读

    用proteus6.7仿真提示cannot find model file bldcm.mdf这是为啥??

    用proteus6.7仿真提示cannot find model file bldcm.mdf这是为啥,大神们帮我看看的,求解答的,求解决问题。。。。
    发表于 05-15 12:24

    魔力非凡——隆宇 黑魔力电源净化器“魔力2500”

    我说他们推出了一款LY-6KW最新改良型,倍频率高达2500倍,型号大致不变是LY-6KW-1,名字叫做魔力2500,说是音效十分出色,比我原来那台要好得多,叫我一定要试一试,作个AB比较云云。有
    发表于 04-02 12:14

    模拟电路有什么魔力

    猜猜看是哪两家半导体厂商?——的公司名称以T开头,过去主要做数字IC,如今却认为模拟电子更精彩且更具报酬效益!对于半导体厂商来说,过去曾经是只要具备扩展数字晶体管的能力,即可提供一个看起来前景无限
    发表于 06-21 04:20

    智能魔镜显示屏是什么,究竟魔力何在

    随着黑科技逐渐渗入到日常生活,人们对科技产品的审视标准日益严格,传统镜子主要功能是被人们用来整理仪容。然而随着科学技术在各个领域中的普及化,镜子的单一功能被改写,旷世智能魔镜显示屏,究竟魔力何在
    发表于 09-02 16:31 842次阅读

    搞嵌入式,为啥要有uboot?

    搞嵌入式的,为啥要有uboot?
    的头像 发表于 02-05 12:00 3082次阅读

    日本人都爱买什么手机?国产手机品牌为啥在日本就打不过当地品牌呢

    则是唯一一个亮眼的国际品牌。 所以,风靡全球的华为、小米等国产手机品牌,为啥在日本就打不过当地品牌呢?苹果赢得市场的秘诀又是啥? 日本人都爱买什么手机? 在许多国人的印象中,日本人最喜欢用的就是翻盖式的功能机,一群穿着校
    的头像 发表于 12-11 17:44 1.4w次阅读

    为什么单相电机要用电容启动?

    为啥单相电机要用电容启动呢?本文小编就来给大家解析一下。
    的头像 发表于 12-14 21:24 1610次阅读

    为啥单相电机要用电容启动?

    为啥单相电机要用电容启动呢?本文小编就来给大家解析一下。
    发表于 03-12 06:06 57次下载
    <b class='flag-5'>为啥</b>单相电机要用电容启动?

    Gartner魔力象限报告中将IBM评为AI领导者

    IBM 在 Gartner 2021年“云 AI 开发者服务魔力象限“和“数据科学与机器学习平台魔力象限”报告中均被评为领导者。 精彩提要 中国北京,2021年 3月 12日 —— IBM
    的头像 发表于 03-19 09:47 1950次阅读

    to B软件为啥用户体验不好

    to B软件为啥用户体验不好?我今天从机制根源层面给大家说说。否则大家还停留在UI、UE的认知层面上。(1)从甲方视角看to B软件其实分为:高层决策软件、中层管理软件、基层业务操作软件。...
    发表于 12-28 19:37 6次下载
    to B软件<b class='flag-5'>为啥</b>用户体验不好

    从蓝光到绿色魔力

    从蓝光到绿色魔力
    发表于 11-02 08:16 0次下载
    从蓝光到绿色<b class='flag-5'>魔力</b>

    国内唯一,华为再次入选2022 Gartner SIEM魔力象限

    2022年10月,业界知名分析机构Gartner公司发布2022年SIEM(Security Information and Event Management,安全信息和事件管理)魔力象限
    的头像 发表于 11-16 19:35 767次阅读

    高速信号为啥要走表层?

    高速信号为啥要走表层?
    的头像 发表于 12-05 15:16 659次阅读
    高速信号<b class='flag-5'>为啥</b>要走表层?

    被众人吐槽的Keil,为啥还能挺到现在?

    这款工具相信大家都不陌生,一直被人吐槽很难用,但它为啥没有被淘汰呢?一、Keil被吐槽的点Keil被大家吐槽最多的还是那千年不变的UI界面,其他很多IDE的界面都比较“现代化”,而K
    的头像 发表于 04-18 08:10 1329次阅读
    被众人吐槽的Keil,<b class='flag-5'>为啥</b>还能挺到现在?