带你深入了解一下vue.js中的this.$nextTick!

news2024/11/17 3:44:29

我们先看看nextTick究竟是个啥?

console.log(this.$nextTick);
// 控制台打印
if(fn){
  return nextTick(fn, this);
}

我们可以看出nextTick就是一个方法,方法有两个参数:fn和this,fn就是需要传的回调函数,this就是所说的执行环境上下文。那么问题来了,在Vue中是如何实现在下一次DOM更新结束之后才会执行延迟回调的? 我们先看看下面的例子:

<div ref="test1">created:{{message}}</div>
// vue实例
data: {
    message: "Hello World!",
},
created(){
    this.message = '你好,世界!';
    console.log(this.$refs.test1.innerText);// 报错
    // TypeError: Cannot read properties of undefined (reading 'innerText')
    this.$nextTick(()=>{
         console.log('test1 nextTick:',this.$refs.test1.innerText);// 你好,世界!
    });
},

从上面例子中,在created生命周期中操作了DOM,但是我们都知道created生命周期只是初始化了数据,这期间是还没有渲染DOM的,如果我们直接操作DOM是找不到DOM元素的,那么问题来了:为什么放在nextTick中就可以获取到了DOM元素呢? 这不是很明显吗,等到DOM渲染完才调用不就获取到了吗,从而知道了nextTick作用就是用来等下次DOM渲染完才去调用nextTick内的DOM操作代码。那么问题又来了,nextTick究竟做了什么?下面我们一起从源码层面来分别分析Vue2和Vue3版本的nextTick原理是啥。

Vue2版本的nextTick

由于Vue暴露给开发者的是nextTick这个方法,在这个方法中主要做了三件事,回调函数的添加延迟执行回调函数判断当前的nextTick是否传入回调函数。不传的话,是一个Promise,this.$nextTick.then(()=>{}),按Promise处理。

 回调函数添加入callbacks数组,因为可能有多个nextTick函数在当前作用域中。

 

 

  • 判断当前nextTick是否已经标记为pending=true,也就是正在执行,如果不是就执行timerFunc(异步执行函数 用于异步延迟调用 flushCallbacks 函数)。timerFunc的执行,判断当前环境是否支持promise、MutationObserver、setImmediate、setTimeout,优先级高低从前到后,分四种情况:
  • 优先使用Promise,如果当前环境支持promise,nextTick默认优先使用promise去执行延迟回调函数,timerFunc执行的是Promise,promise是es6下的语法,如果当前环境只支持es6以语法下的,只能考虑后面支持情况了。

 

 

  • 支持MutationObserver,HTML5的api,中文意思是:修改的监听,MutationObserver用来监听DOM的变动,比如节点的增减、属性的变动、文本内容的修改等都会触发MutationObserver事件。注意地,与事件不同,事件是同步触发,DOM的变动会立即触发事件,而MutationObserver事件是异步触发,DOM不会立即触发,需要等当前所有DOM操作完毕才会触发。

MutationObserver有7个属性:childList(true,监听子节点的变动)、attributes(true,监听属性的变动)、characterData(true,监听节点内容或节点文本的变动)、subtree(是否应用于该节点的所有后代节点)、attributeOldValue(观察attributes变动时,是否需要记录变动前的属性值)、characterDataOldValue(观察characterData变动时,是否需要记录变动前的值)、attributeFilter(数组,表示需要观察的特定属性(比如[‘class’,‘src’])。

为什么需要创建一个文本节点?因为在这里操作DOM保证浏览器页面是最新DOM渲染的,虽然看来好像是没什么作用,但这是保证拿到的DOM是最新的。

 

 支持setImmediatesetTimeout,setImmediate即时计时器立即执行工作,它是在事件轮询之后执行,为了防止轮询阻塞,每次只会调用一个。setTimeout按照一定时间后执行回调函数。

 

 好了好了,到了现在,我们都知道nextTick做了什么吧,但是我们有没有想过这样的一个问题:既然都是异步回调执行等待DOM更新后才去调用操作DOM的代码,那么这个机制又是什么原理?这就是JS的执行机制有关了,涉及宏任务与微任务的知识,我们先来看看这样的一道题:

console.log('同步代码1');
setTimeout(function () {
    console.log("setTimeout");
}, 0);
new Promise((resolve) => {
    console.log('同步代码2')
    resolve()
}).then(() => {
    console.log('promise.then')
})
console.log('同步代码3');

 我们可能会问上面的输出是个啥,首先js是单线程,所以在js程序运行中只有一个执行栈,实现不了多线程,所以就需要任务均衡分配,通俗的讲,按任务急优先处理原则,js中分为同步任务和异步任务,异步任务又分为宏任务和微任务,同步任务先入栈,程序会先把执行栈中的所有同步任务执行完,再去判断是否有异步任务,而异步任务中微任务的优先级高于宏任务。如果当前执行栈为空,而微任务队列不为空,就先执行微任务,等把所有微任务执行完,最后才会考虑宏任务。而上面代码中Promise是属于微任务,而setTimeout是宏任务,所以上面的输出为:

// 同步代码1
// 同步代码2
// 同步代码3
// promise.then 
// setTimeout

使用Vue2的nextTick

  • 传入回调函数参数使用:

    this.$nextTick(()=>{
      // ...操作DOM的代码
    })

    不传入回调函数参数使用:

// 方式一
this.$nextTick().then(()=>{
  // ...操作DOM的代码
})
// 方式二
await this.$nextTick();
// 后写操作DOM的代码

 

Vue3版本的nextTick

Vue3版本就没有Vue2版本的那么多环境支持,nextTick封装成了一个Promise异步回调函数执行。

// Vue3.2.45
// core-main\core-main\packages\runtime-core\src
export function nextTick<T = void>(
  this: T,
  fn?: (this: T) => void
): Promise<void> {
  const p = currentFlushPromise || resolvedPromise
  return fn ? p.then(this ? fn.bind(this) : fn) : p
}

 

使用Vue3的nextTick

  • 传入回调函数使用

import { nextTick } from 'vue' // 引入
setup () {    
nextTick(()=>{
   // ...操作DOM的代码
})

 不传入回调函数的使用

import { nextTick } from 'vue' // 引入
setup () {    
    // 方式一
    nextTick().then(()=>{
      // ...操作DOM的代码
    })
    // 方式二
    await nextTick();
    // 后写操作DOM的代码
}

 

总结

  • nextTick可以通俗的当作一个Promise,所以nextTick属于微任务。
  • nextTick在页面更新数据后,DOM更新,可以通俗理解为,nextTick就是用来支持操作DOM的代码及时更新渲染页面。也就是在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的DOM结构的时候,这个操作都应该放进Vue.nextTick()的回调函数中。
  • 在Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/107756.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

主轴承盖螺栓拧紧机PLC控制程序

HMI为西门子TP900触摸屏&#xff0c;支持屏幕触摸和按键操作 设备主要参数 设备外形尺寸&#xff1a;长*宽*高 2180*1900*2500mm 生产节拍&#xff1a; 55 S 电源电压&#xff1a; AC380V5%&#xff0c;50HZ&#xff0c;三相五线制 系统组态 常见故障处理 气缸报警 报警原…

Windows下安装VTK8.2.0

Windows下安装VTK8.2.0 1、依赖 VS2017 Qt5 cmake 2、前期准备 2.1、访问vtk官方下载VTK8.2.0源码 VTK源码下载地址&#xff1a;https://vtk.org/download/ 2.2、配置环境变量 配置CMAKE_PREFIX_PATH&#xff0c;值为Qt的bin路径 2.3、新建2个文件夹一个用于存放cm…

11 个有用的现代 JavaScript 技巧

在我们日常开发工作中&#xff0c;我们经常使用到字符串的转换、检查它是否存在的对象中的键、有条件地操作对象数据、过滤数组中的假值等。 在这里&#xff0c;我整理了一些很棒的JavaScript的技巧&#xff0c;这些技巧是我个人最喜欢的&#xff0c;因为它使我的代码更短更干…

亚马逊云科技:还在苦于ETL?Zero ETL的时代已全面到来

在2022亚马逊云科技re:Invent全球大会上&#xff0c;亚马逊云科技数据和机器学习副总裁Swami Sivasubramanian表示&#xff1a;“当前&#xff0c;客户管理的数据既庞大又复杂&#xff0c;这意味着他们不能只用单一技术或几个工具来分析和探索这些数据。在此次2022亚马逊云科技…

Java反射和new效率差距有多大?

1、创建对象的两种方式 //new 方式创建对象 ReflectDemo reflectDemo new ReflectDemo();//反射创建对象 反射创建对象的三种方式 (1)Class<ReflectDemo> reflectDemoClass ReflectDemo.class; (2)Class<?> aClass Class.forName ("com.whale.springtra…

网络ping不通,试试这8招

摘要&#xff1a;网络ping不通&#xff0c;该怎么办&#xff1f;本文教你8个大招&#xff0c;轻松找到问题根源。本文分享自华为云社区《网络ping不通&#xff0c;该怎么办&#xff1f;》&#xff0c;作者&#xff1a;wljslmz。 如下图&#xff0c;PC&#xff08;192.168.10.1…

使用windows系统给C盘分盘

前言 一般我们使用新电脑的时候&#xff0c;有的时候默认给我们分好了盘&#xff0c;有时候只会把全部的内存都放到C盘&#xff0c;这样就需要我们自己手动进行分配资源和分配其他硬盘资源 今天公司邮寄的新电脑到了&#xff0c;正好属于后者&#xff0c;借助这个机会分享下我…

【C语言进阶】六.预处理

&#xff08;1&#xff09;程序的翻译环境和执行环境 在ANSI C的任何一种实现中&#xff0c;存在两个不同的环境。 第1种是翻译环境&#xff0c;在这个环境中源代码被转换为可执行的机器指令。包含编译加链接第2种是执行环境&#xff0c;它用于实际执行代码。 &#xff08;2…

KingbaseES V8R6集群运维案例---数据块故障自动修复(auto_bmr)

案例说明&#xff1a; 在Oracle11.2版本之后&#xff0c;DataGuard 若搭建实时应用日志的物理备库&#xff0c;那么在主库数据文件少 量坏块的情况下&#xff0c;可以利用ABCR技术快速修复坏块。 Starting in Oracle Database 11g Release 2 (11.2), the primary database auto…

高通Ride软件开发包使用指南(12)

高通Ride软件开发包使用指南&#xff08;12&#xff09;9 PCIe交换机9.1简介9.2 PCIe交换机管理9.2.1工具9.2.2 COM端口和COM端口设置9.2.3更新固件和配置9.2.3.1工具设置9.2.3.2 Platform setup9.2.3.3交换机更新9 PCIe交换机 9.1简介 在Snapdragon Ride的高级配置中&#x…

四、基于kubeadm安装kubernetes1.25集群第三篇

接上篇&#xff1a;https://blog.csdn.net/u011837804/article/details/128355649 1、安装 kubeadm、kubelet 和 kubectl 1.1、配置yum源 cat > /etc/yum.repos.d/kubernetes.repo << EOF [kubernetes] nameKubernetes baseurlhttps://mirrors.aliyun.com/kubernet…

算法| Java的int类型最大值为什么是21亿多?

开篇 本文主要介绍在Java中&#xff0c;为什么int类型的最大值为2147483647。 理论值 我们都知道在Java中&#xff0c;int 的长度为32位。 理论上&#xff0c;用二进制表示&#xff0c;32位每一位都是1的话&#xff0c;那么这个数是多少呢&#xff1f; 我们来计算一下&…

【QGIS入门实战精品教程】10.1:QGIS基于DEM数据的地形分析案例教程

本文讲解QGIS中基于DEM数据的地形分析方法,包括:坡度分析、坡向分析、山体阴影、地貌分析、强度指数(地形复杂性)。 文章目录 一、加载DEM二、坡度分析三、坡向分析四、山体阴影五、地貌分析六、强度指数(地形复杂性)QGIS提供了地形分析的工具,位于工具箱→Raster terrain a…

录屏怎么录声音?电脑录视频怎么带声音?图文教学来了

电脑录屏功能的应用&#xff0c;给我们的日常生活带来了便利。随着电脑录屏功能的应用&#xff0c;“电脑录屏”随着功能的升级&#xff0c;电脑屏幕录制不仅仅是简单的屏幕录制&#xff0c;还有很多额外的功能&#xff0c;比如录制带声音的视频&#xff01;那么录屏怎么录声音…

[C++]类和对象【下】

&#x1f941;作者&#xff1a; 华丞臧 &#x1f4d5;​​​​专栏&#xff1a;【C】 各位读者老爷如果觉得博主写的不错&#xff0c;请诸位多多支持(点赞收藏关注)。如果有错误的地方&#xff0c;欢迎在评论区指出。 推荐一款刷题网站 &#x1f449;LeetCode 文章目录再谈构造…

结构建模设计——Solidworks软件之装配体操作基本总结一(装配体功能界面简介、插入零件操作、基本配合操作)

【系列专栏】&#xff1a;博主结合工作实践输出的&#xff0c;解决实际问题的专栏&#xff0c;朋友们看过来&#xff01; 《QT开发实战》 《嵌入式通用开发实战》 《从0到1学习嵌入式Linux开发》 《Android开发实战》 《实用硬件方案设计》 长期持续带来更多案例与技术文章分享…

漏洞猎人白夜追凶记

编者按 数字化浪潮蓬勃兴起&#xff0c;企业面临的安全挑战亦日益严峻。 腾讯安全近期将复盘2022年典型的攻击事件&#xff0c;帮助企业深入了解攻击手法和应对措施&#xff0c;完善自身安全防御体系。 本篇是第三期&#xff0c;用一个实际案例讲述了企业在面临攻击时&#xf…

【Java枚举类与注解】——一篇文章读懂枚举类与注解

文章目录2.枚举2.1概述2.2定义格式2.3枚举的特点2.4枚举的方法3.注解3.1概述3.2自定义注解3.3 元注解2.枚举 2.1概述 为了间接的表示一些固定的值&#xff0c;Java就给我们提供了枚举&#xff0c;是指将变量的值一一列出来&#xff0c;变量的值只限于列举出来的值的范围内。 …

C. Arpa’s overnight party and Mehrdad’s silent entering(构造+二分图)

Problem - 741C - Codeforces 请注意&#xff0c;阿尔帕的土地上的女孩真的很有吸引力。 阿尔帕喜欢过夜的聚会。在一次聚会中&#xff0c;迈尔达德突然出现。他看到n对朋友围坐在一张桌子旁。第i对包括一个男孩&#xff0c;坐在第i把椅子上&#xff0c;和他的女朋友&#xff…

【LeetCode每日一题】——264.丑数 II

文章目录一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【解题思路】七【题目提示】八【时间频度】九【代码实现】十【提交结果】一【题目类别】 动态规划 二【题目难度】 中等 三【题目编号】 264.丑数 II 四【题目描述】 给你一个整数 n …