【Vue】— nextTick原理

2023-11-10

前言

在做项目的时候,我们经常会用到nextTick,简单的理解就是它就是一个setTimeout函数,将函数放到异步后去处理;将它替换成setTimeout好像也能跑起来,但它仅仅这么简单吗?那为什么我们不直接用setTimeout呢?让我们深入剖析一下。

友情提示:在开启本文前,需要对JS事件循环有一定了解;如果对事件循环还不了解的小伙伴,先去了了解一下

发现问题

记得之前有一个需求,就是根据文字的行数来显示展开更多的一个按钮,因此我们在Vue中给数据赋值之后需要获取文字高度。
在这里插入图片描述
这时不管怎么获取,文字的Div高度都是0;但是直接获取却是有值:
在这里插入图片描述
 同样的情况也发生在给子组件传参上;我们给子组件传参数后,在子组件中调用函数查看参数。
在这里插入图片描述
 虽然页面上展示了子组件的name,但是打印出来却是空值:
在这里插入图片描述

异步更新

我们发现上述两个问题的发生,不管子组件还是父组件,都是在给data中赋值后立马去查看数据导致的。由于“查看数据”这个动作是同步操作的,而且都是在赋值之后;因此我们猜测一下,给数据赋值操作是一个异步操作,并没有马上执行,Vue官网对数据操作是这么描述的:

可能你还没有注意到,Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。Vue 在内部对异步队列尝试使用原生的 Promise.then、MutationObserver 和 setImmediate,如果执行环境不支持,则会采用 setTimeout(fn, 0) 代替。

也就是说我们在设置this.msg = 'some thing'的时候,Vue并没有马上去更新DOM数据,而是将这个操作放进一个队列中;如果我们重复执行的话,队列还会进行去重操作;等待同一事件循环中的所有数据变化完成之后,会将队列中的事件拿出来处理。

这样做主要是为了提升性能,因为如果在主线程中更新DOM,循环100次就要更新100次DOM;但是如果等事件循环完成之后更新DOM,只需要更新1次。

为了在数据更新操作之后操作DOM,我们可以在数据变化之后立即使用Vue.nextTick(callback);这样回调函数会在DOM更新完成后被调用,就可以拿到最新的DOM元素了。
  在这里插入图片描述

nextTick源码分析

了解了nextTick的用法和原理之后,我们就来看一下Vue是怎么来实现这波“操作”的。在这里插入图片描述
VuenextTick的源码单独抽到一个文件中,/src/core/util/next-tick.js,删掉注释也就大概六七十行的样子,让我们逐段来分析。
在这里插入图片描述
我们首先找到nextTick这个函数定义的地方,看看它具体做了什么操作;看到它在外层定义了三个变量,有一个变量看名字就很熟悉:callbacks,就是我们上面说的队列;在nextTick的外层定义变量就形成了一个闭包,所以我们每次调用$nextTick的过程其实就是在向callbacks新增回调函数的过程。
  callbacks新增回调函数后又执行了timerFunc函数,pending用来标识同一个时间只能执行一次。那么这个timerFunc函数是做什么用的呢,我们继续来看代码:
在这里插入图片描述
这里出现了好几个isNative函数,这是用来判断所传参数是否在当前环境原生就支持;例如某些浏览器不支持Promise,虽然我们使用了垫片(polify),但是isNative(Promise)还是会返回false

可以看出这边代码其实是做了四个判断,对当前环境进行不断的降级处理,尝试使用原生的Promise.then、MutationObserver和setImmediate,上述三个都不支持最后使用setTimeout;降级处理的目的都是将flushCallbacks函数放入微任务(判断1和判断2)或者宏任务(判断3和判断4),等待下一次事件循环时来执行。MutationObserverHtml5的一个新特性,用来监听目标DOM结构是否改变,也就是代码中新建的textNode;如果改变了就执行MutationObserver构造函数中的回调函数,不过是它是在微任务中执行的。

那么最终我们顺藤摸瓜找到了最终的大boss:flushCallbacksnextTick不顾一切的要把它放入微任务或者宏任务中去执行,它究竟是何方神圣呢?让我们来一睹它的真容:
在这里插入图片描述
本来以为有多复杂的flushCallbacks,居然不过短短的8行。它所做的事情也非常的简单,把callbacks数组复制一份,然后把callbacks置为空,最后把复制出来的数组中的每个函数依次执行一遍;所以它的作用仅仅是用来执行callbacks中的回调函数。

总结

整体nextTick的代码都分析完毕了,总结一下它的流程就是:

  1. 把回调函数放入callbacks等待执行
  2. 通过pending判断是不是已经执行过;
  3. 将执行函数放到微任务或者宏任务中
  4. 事件循环到了微任务或者宏任务,执行函数依次执行callbacks中的回调

再回到我们开头说的setTimeout,可以看出来nextTick是对setTimeout进行了多种兼容性的处理,宽泛的也可以理解为将回调函数放入setTimeout中执行;不过nextTick优先放入微任务执行,而setTimeout是宏任务。

文章转载于:面试题:Vue中$nextTick原理

补充MutationObserver

MutationObserverHTML5新增的属性,用于监听DOM修改事件,能够监听到节点的属性、文本内容、子节点等的改动,是一个功能强大的利器,基本用法如下:

//MO基本用法
var observer = new MutationObserver(function(){
  //这里是回调函数
  console.log('DOM被修改了!');
});
var article = document.querySelector('article');
observer.observer(article);

MutationObserver属于microtask

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Vue】— nextTick原理 的相关文章

  • 系统滴答定时器(SYSTICK)

    一 SysTick定时器 SysTick定时器被捆绑在NVIC中 用于产生SYSTICK异常 他的作用是为各个不同任务许以不同数目的时间片 确保没有一个任务能霸占系统 或者把每个定时器周期 的某个时间范围赐予特定的任务等 还有操作系统提供的

随机推荐

  • TypeError: Cannot interpolate with all object-dtype columns in the DataFrame

    目录 1 问题说明 1 问题说明 Traceback most recent call last File D python3 8 5 lib multiprocessing pool py line 125 in worker resul
  • 基于STM32使用HAL库手把手移植华为LiteOS系统并使用CMSIS接口API

    基于STM32使用HAL库手把手移植华为LiteOS系统并使用CMSIS接口API 目录 基于STM32使用HAL库手把手移植华为LiteOS系统并使用CMSIS接口API 一 说明 二 裸机工程创建 三 移植LiteOS 四 系统运行测试
  • 2018全球智能手机市场的主要趋势

    2018全球智能手机市场的主要趋势
  • 【node进阶】深度解析Express框架--路由、中间件

    作者简介 一名普通本科大三的学生 致力于提高前端开发能力 个人主页 前端小白在前进的主页 系列专栏 node js学习专栏 个人社区 个人交流社区 学习格言 打不倒你的会使你更强 前言 国庆期间 也不能停止学习 上一篇文章讲解了expres
  • bash下的单方括号和双方括号

    bash下的单方括号和双方括号概念以及用法 一 bash 单括号 两个符号左右都要有空格分隔 内部操作与操作变量之间要有空格 val gt10 字符
  • numpy.array中获取特定元素下标方法

    例 寻找array x 中 数值为0的所有下标 并转换为np array index np array np where x 0
  • JVM基础-Hotspot VM相关知识学习

    这里写目录标题 jdk JVM虚拟机 类 类的生命周期 类加载的时机 类的双亲委派机制 类的验证 java对象 Mark Word Klass Pointer 实例数据 对齐数据 字符串常量池 垃圾收集器 1 Serial收集器 串行收集器
  • 面试官:如何实现大文件上传

    问题背景 如果将大文件一次性上传 耗时会非常长 甚至可能传输失败 那么我们怎么解决这个问题呢 既然大文件上传不适合一次性上传 那么我们可以尝试将文件分片散上传 这样的技术就叫做分片上传 分片上传就是将大文件分成一个个小文件 切片 将切片进行
  • 安装Windows 10与Ubuntu 16.04双系统后无法启动Windows

    安装Windows 10与Ubuntu 16 04双系统后无法启动Windows 10 在这里把解决办法记录下来 以备查阅 办法1 尝试利用GRUB自动修复系统引导项 运行以下命令 让grub自动更新系统引导项 sudo update gr
  • java鼠标钩子实现

    java本身是没有提供直接访问系统的API方法 查了下资料 要实现鼠标钩子有3种方法 1 使用JNI方法 需要自己写C实现 比较复杂 2 使用swt extension 直接提供了鼠标钩子的实现 但是用它调用其它windows api 没有
  • 用c语言编写出三底模型

    以下是一个用C语言实现三底模型的示例代码 这个程序通过循环遍历输入的股票数据 判断是否出现三底形态 如果是 则输出买入信号 否则输出卖出信号 c语言 include
  • Matlab绘图保存为.fig格式以使用,及.fig文件的加载与数据读取

    1 使用背景 fig文件作为Matlab中的图形文件 其实原始数据是会存储在figure对象中的 其不仅仅只是一张图片 论文写作时 直接将plot运行的结果进行截图粘贴时 图片的分辨率不够导致曲线及图片上的文字模糊 plot运行得到的坐标轴
  • wireshark实战之局域网抓包分析

    Wireshark 它是一款本地监听数据的大杀器 弊端是只能监听本地的数据 有什么办法可以让局域网中的流量都从本机走呢 第一ARP嗅探 劫持网关 再本地抓包 第二交换机镜像端口 在路由或者交换机处设置 复制一份数据到指定机子端口 第三Ap一
  • mysql 设置男女约束

    参考 MySQL数据库创建sex字段设置男女约束 这样写好像会因为汉字而报错 Invalid default value for sex CREATE TABLE t user id INT 11 NOT NULL AUTO INCREME
  • js上传视频获取第一帧截图

    1 获取视频的url 如果是file文件 先转成url file转url方法 function getFileURL file var url null if window createObjectURL undefined basic u
  • Harbor2.1私服的搭建

    Harbor2 1私服的搭建 基础环境的搭建 替换国内yum源 curl o etc yum repos d CentOS Base repo http mirrors aliyun com repo Centos 7 repo yum m
  • CTF-Web小白入门篇超详细——了解CTF-Web基本题型及其解题方法 总结——包含例题的详细题解

    很久之前经过一次比赛的打击 决定不能只是一直盲目的刷基础题 应该加快进度 从各种基本题型开始下手 每种题型都应该去找题目刷一刷 并做好总结 于是我去搜了一些总结的文章 便找到了一篇很好的 觉得看完后有点豁然开朗的感觉 所以本文是在该文章的基
  • Th4.6:模板全特化、偏特化(局部特化)详述

    本小节回顾的知识点分别是模板全特化 偏特化 局部特化 今天总结的知识分为以下4个大点 1 特化与泛化 2 类模板特化 2 1 类模板全特化 a 常规全特化 b 特化模板类的成员函数而不是类模板本身 2 2 类模板偏特化 局部特化 a 从模板
  • IP175G参考资料和引脚图

    特性 5端口嵌入式10 100PHY开关控制器 支持5100BaseTX IP175G 或4100BaseTX 85nm 技术 只需要3 3V单通道 1FX IP175GH 支持1KMAC地址表项 448K位包缓冲存储器 100MPHY支持
  • 【Vue】— nextTick原理

    文章目录 前言 发现问题 异步更新 nextTick源码分析 总结 补充 MutationObserver 前言 在做项目的时候 我们经常会用到nextTick 简单的理解就是它就是一个setTimeout函数 将函数放到异步后去处理 将它