Vue 中为什么要有nextTick

本文分享自华为云社区《Vue 中的 nextTick 有什么作用?》,作者:CoderBin。

一、什么是nextTick

先看看官方对其的定义:

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

什么意思呢?

我们可以理解成,Vue 在更新 DOM 时是异步执行的。当数据发生变化,Vue将开启一个异步更新队列,视图需要等队列中所有数据变化完成之后,再统一进行更新

举个例子:

Html结构

{{ message }}

构建一个vue实例

const vm = new Vue({el: ‘#app’,data: {message: ‘原始值’}})

修改message

this.message = ‘修改后的值1’ this.message = ‘修改后的值2’ this.message = ‘修改后的值3’

这时候想获取页面最新的DOM节点,却发现获取到的是旧值

console.log(vm.$el.textContent) // 原始值

这是因为message数据在发现变化的时候,vue并不会立刻去更新Dom,而是将修改数据的操作放在了一个异步操作队列中

如果我们一直修改相同数据,异步操作队列还会进行去重

等待同一事件循环中的所有数据变化完成之后,会将队列中的事件拿来进行处理,进行DOM的更新

为什么要有nexttick

举个例子

{{num}} for(let i=0; i<100000; i++){ num = i }

如果没有 nextTick 更新机制,那么 num 每次更新值都会触发视图更新(上面这段代码也就是会更新10万次视图),有了nextTick机制,只需要更新一次,所以nextTick本质是一种优化策略

二、使用场景

如果想要在修改数据后立刻得到更新后的DOM结构,可以使用Vue.nextTick()

  • 第一个参数为:回调函数(可以获取最近的DOM结构)
  • 第二个参数为:执行函数上下文

// 修改数据vm.message = ‘修改后的值’// DOM 还没有更新console.log(vm.$el.textContent) // 原始的值Vue.nextTick(function () {// DOM 更新了console.log(vm.$el.textContent) // 修改后的值})

组件内使用 vm.$nextTick() 实例方法只需要通过this.$nextTick(),并且回调函数中的 this 将自动绑定到当前的 Vue 实例上

this.message = ‘修改后的值’console.log(this.$el.textContent) // => ‘原始的值’this.$nextTick(function () {console.log(this.$el.textContent) // => ‘修改后的值’})

$nextTick() 会返回一个 Promise 对象,可以是用async/await完成相同作用的事情

this.message = ‘修改后的值’console.log(this.$el.textContent) // => ‘原始的值’await this.$nextTick()console.log(this.$el.textContent) // => ‘修改后的值’三、实现原理

源码位置:/src/core/util/next-tick.js

callbacks也就是异步操作队列

callbacks新增回调函数后又执行了timerFunc函数,pending是用来标识同一个时间只能执行一次

export function nextTick(cb?: Function, ctx?: Object) {let _resolve;// cb 回调函数会经统一处理压入 callbacks 数组callbacks.push(() => {if (cb) {// 给 cb 回调函数执行加上了 try-catch 错误处理try {cb.call(ctx);} catch (e) {handleError(e, ctx, ‘nextTick’);}} else if (_resolve) {_resolve(ctx);}});// 执行异步延迟函数 timerFuncif (!pending) {pending = true;timerFunc();}// 当 nextTick 没有传入函数参数的时候,返回一个 Promise 化的调用if (!cb && typeof Promise !== ‘undefined’) {return new Promise(resolve => {_resolve = resolve;});}}

timerFunc函数定义,这里是根据当前环境支持什么方法则确定调用哪个,分别有:

Promise.then、MutationObserver、setImmediate、setTimeout

通过上面任意一种方法,进行降级操作

export let isUsingMicroTask = falseif (typeof Promise !== ‘undefined’ && isNative(Promise)) {//判断1:是否原生支持Promiseconst 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]’)) {//判断2:是否原生支持MutationObserverlet counter = 1const observer = new MutationObserver(flushCallbacks)const textNode = document.createTextNode(String(counter))observer.observe(textNode, {characterData: true})timerFunc = () => {counter = (counter + 1) % 2textNode.data = String(counter)}isUsingMicroTask = true} else if (typeof setImmediate !== ‘undefined’ && isNative(setImmediate)) {//判断3:是否原生支持setImmediatetimerFunc = () => {setImmediate(flushCallbacks)}} else {//判断4:上面都不行,直接用setTimeouttimerFunc = () => {setTimeout(flushCallbacks, 0)}}

无论是微任务还是宏任务,都会放到flushCallbacks使用

这里将callbacks里面的函数复制一份,同时callbacks置空

依次执行callbacks里面的函数

function flushCallbacks () {pending = falseconst copies = callbacks.slice(0)callbacks.length = 0for (let i = 0; i < copies.length; i++) {copies[i]()}}四、最后总结

  • 把回调函数放入callbacks等待执行
  • 将执行函数放到微任务或者宏任务中
  • 事件循环到了微任务或者宏任务,执行函数依次执行callbacks中的回调
  • 本文内容及图片均整理自互联网,不代表本站立场,版权归原作者所有,如有侵权请联系admin#jikehao.com删除。
    (0)
    极客号的头像极客号
    上一篇 2022年 10月 17日
    下一篇 2022年 10月 17日

    相关推荐

    • 免冠证件照是什么意思(免冠)

      1、免冠的意思是脱帽,古时表示谢罪,后来表示敬意不戴帽子免冠 miǎn guān 详细释义 1脱帽古人用以表示谢罪战国策·齐策六“ 田单 免冠徒跣肉袒而进,退而请死罪” 唐 韩愈 …

      2023年 6月 9日
    • 日本男明星颜值排行榜前十名(日本男明星)

        微信搜索:贯通日语听力,每天推送日语听力资料。   樱井翔   原名 櫻井翔   小名 翔くん、翔さん、翔ちゃん   出生年月 1982年1月25日(27歳)   出生地 東京…

      2023年 8月 17日
    • 计划怀孕前要解决哪些牙齿问题?

      经常有朋友打电话来咨询,老婆怀孕了,牙齿痛得不得了,该怎么办?孕期女性看牙确实有诸多不便,那么在怀孕前牙科方面应该做哪些准备呢? 准备怀孕的女性在备孕时一定去看一下牙科医生。 哪些…

      2022年 10月 12日
    • 旅游购物的套路

      许多年前梦想着退休之后就可以去全球旅游,可是真正到了两口子都没事的时候,疫情却把我们阻隔在了很小的范围内,基本上每天都是和朋友们一起渡过,下午先掼蛋晚上就是喝酒。老两口晕头转向地回…

      2022年 9月 28日
    • 最恐怖的恐怖片排行(最恐怖的恐怖片排名)

      最强恐怖片排行榜 根据我的了解总结最强恐怖片排行榜前十名有《活僵尸之夜》,《午夜凶铃,《鬼影》,《蔷花红莲》,《死神的十字路口》,《怪形》,《异形2》,《异形3》,《异形4》,《异…

      2024年 1月 9日
    • 世乒赛淘汰赛抽签揭晓,中国男队或提前迎来重大考验

      4日晚,第56届世界乒乓球团体锦标赛(决赛)进行了淘汰赛抽签,中国男队与本次世乒赛目前来看最强的竞争对手瑞典队落入了同一半区,两队或将在四分之一决赛遭遇。 瑞典队阵中除了休斯敦世乒…

      2022年 10月 5日
    • 聚光计划_何音_2022年09月30日_微头条

      家乡的“糖油果子”挂满了我的回忆。好想回到童年,孩子们拿着爸妈给的零钱拥向那位挑着担子走街串巷叫卖“糖油果子”的老爷爷,迫不及待从老爷爷手中接过一串圆圆的、油亮亮的、香甜松软的糖油…

      2022年 10月 1日
    • 寻腾蛟之美,享诗意山水,赏秋日美景!

      古镇腾蛟,原名郑家堡,清光绪初,集镇已具相当规模,因人姓众多,大家深感地名与实际情况不符,一些乡绅文人取”山川毓秀,腾蛟起凤”之意,且闽南话中”…

      2022年 10月 10日
    • 微信运动在哪里开启和关闭(微信运动在哪里)

           上海有爱吗-43   为它们找一个温暖的家 一犬一话与Think Adoption合作,每周推送一些领养信息。提倡领养代替买卖,因我们相信:金钱买不到真正的缘分,而缘分无…

      2023年 10月 6日
    • 陪读宝妈的心酸与无奈

      我是一个80后陪读宝妈,从九月一号开始我正式成为了一名“高一”男孩的陪读宝妈。 从开学到现在已经有一个多月的时间了,在这段时间里我的生活也随之发生了变化。其一,生活上的变化,从夫妻…

      2022年 10月 11日

    合作联系:1152QQ041010