【揭秘Vue】nextTick的神秘面纱:原理与作用一览无余!

本文主要是介绍【揭秘Vue】nextTick的神秘面纱:原理与作用一览无余!,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言 - JavaScript执行原理EventLoop

JavaScript执行原理涉及到Event Loop(事件循环)的概念。JavaScript是一种单线程语言,意味着它一次只能执行一个任务。然而,JavaScript经常需要处理异步任务,比如网络请求、定时器等,而这些任务的执行不能阻塞主线程。这时候就需要Event Loop来协调异步任务的执行。

Event Loop的基本工作原理:

  1. 执行栈(Call Stack): 执行栈是存储函数调用的栈结构。当一个函数被调用,它就会被推入执行栈,执行完毕后就会被弹出。这是一个同步执行的过程。
  2. 消息队列(Message Queue): 消息队列用来存放异步任务的回调函数。当异步任务完成后,会将其回调函数放入消息队列
  3. 事件循环(Event Loop): Event Loop是一个持续运行的循环,负责检查执行栈和消息队列。当执行栈为空时,Event Loop会检查消息队列是否有待执行的任务。如果有,就将任务的回调函数推入执行栈执行。这个过程一直循环进行,形成了事件循环。
  4. 宏任务(Macro Task)和微任务(Micro Task): 在事件循环中,任务分为宏任务和微任务。宏任务包括整体的script代码、setTimeout、setInterval等,而微任务包括Promise、process.nextTick等。微任务的优先级高于宏任务,会在当前宏任务执行完毕后立即执行。

整个执行过程可以简要描述为:

  • 执行栈执行同步任务。
  • 当遇到异步任务时,将其回调函数加入消息队列。
  • 当执行栈为空时,Event Loop检查消息队列。
  • 如果消息队列中有任务,就将任务的回调函数推入执行栈执行。
  • 重复以上过程。

下面是一个简单的例子,演示Event Loop的执行过程:

console.log('Start');setTimeout(function() {console.log('Timeout callback');
}, 0);Promise.resolve().then(function() {console.log('Promise callback');
});console.log('End');

在上面的例子中,执行顺序是:

  1. 打印 ‘Start’
  2. 打印 ‘End’
  3. 打印 ‘Promise callback’
  4. 打印 ‘Timeout callback’

这是因为Promise的回调函数属于微任务,它会在当前宏任务执行完毕后立即执行,而setTimeout的回调函数属于宏任务,会在下一个宏任务中执行。

$nextTick的原理

Vue的nextTick其本质是对JavaScript执行原理EventLoop的一种应用。

nextTick的核心是利用了如PromiseMutationObserversetImmediatesetTimeout等原生JavaScript方法来模拟对应的微/宏任务的实现,本质是为了利用JavaScript的这些异步回调任务队列来实现Vue框架中自己的异步回调队列

具体的解释

nextTick 是 Vue.js 中一个重要的异步更新机制,它用于在 DOM 更新之后执行回调函数。Vue.js通过nextTick来确保在数据变化后,DOM 已经更新。

JavaScript 的事件循环(Event Loop)分为宏任务队列(macrotask queue)和微任务队列(microtask queue)。nextTick的实现利用了这两个队列,以确保在 DOM 更新后执行回调。

在 Vue.js 中,当数据发生变化时,Vue 异步执行 DOM 更新。在这个过程中,Vue 会将需要执行的回调函数推入一个队列中,而不是立即执行它们。这就是 nextTick 的核心机制。

在现代浏览器中,Vue 使用 PromiseMutationObserver 来模拟微任务。如果两者都不可用,Vue 会回退到使用 setTimeout 来模拟宏任务。

下面是一个简化的 nextTick 实现,用于说明其基本原理:

let callbacks = [] //用于存储待执行的回调函数。
let pending = false //用于标识是否已经将回调函数推入队列,避免重复推入。function flushCallbacks() { //用于执行队列中的所有回调函数。pending = falseconst copies = callbacks.slice(0)callbacks.length = 0for (let i = 0; i < copies.length; i++) {copies[i]()}
}function nextTick(cb) { //用于将回调函数推入队列,并在适当的时机执行。callbacks.push(cb)if (!pending) {pending = trueif (typeof Promise !== 'undefined') {Promise.resolve().then(flushCallbacks)} else if (typeof MutationObserver !== 'undefined') {const observer = new MutationObserver(flushCallbacks)const textNode = document.createTextNode('1')observer.observe(textNode, { characterData: true })textNode.data = '2'} else if (typeof setImmediate !== 'undefined') {setImmediate(flushCallbacks)} else {setTimeout(flushCallbacks, 0)}}
}

nextTick 在不同环境下使用了不同的异步执行机制,确保了在现代浏览器和旧版本浏览器中都能够正常工作。这样,Vue 能够在数据变化后,等待 DOM 更新完成后执行用户传入的回调函数,从而实现更可靠的异步更新机制。

$nextTick的作用

nextTick不仅是Vue内部的异步队列的调用方法,同时也允许开发者在实际项目中使用这个方法来满足实际应用中对DOM更新数据时机的后续逻辑处理。

nextTick是典型的将底层JavaScript执行原理应用到具体案例中的示例,引入异步更新队列机制的原因:

  • 如果是同步更新,则多次对一个或多个属性赋值,会频繁触发UI/DOM的渲染,可以减少一些无用渲染。
  • 同时由于VirtualDOM的引入,每一次状态发生变化后,状态变化的信号会发送给组件,组件内部使用VirtualDOM进行计算得出需要更新的具体的DOM节点,然后对DOM进行更新操作。每次更新状态后的渲染过程需要更多的计算,而这种无用功也将浪费更多的性能,所以异步渲染变得更加至关重要。

Vue采用了数据驱动视图的思想,但是在一些情况下,仍然需要操作DOM。有时候,可能遇到这样的情况,DOM1的数据发生了变化,而DOM2需要从DOM1中获取数据,那这时就会发现DOM2的视图并没有更新,这时就需要用到了nextTick了。

由于Vue的DOM操作是异步的,所以,在上面的情况中,就要将DOM2获取数据的操作写在$nextTick中。

所以,在以下情况下,会用到nextTick

  • 在数据变化后执行的某个操作,而这个操作需要使用随数据变化而变化的DOM结构的时候,这个操作就需要方法在nextTick()的回调函数中。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Vue nextTick Example</title>
</head>
<body><div id="app"><p>{{ message }}</p><button @click="changeMessage">Change Message</button><div v-if="showMessage">DOM Updated: {{ updatedMessage }}</div>
</div><script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
<script>
new Vue({el: '#app',data: {message: 'Hello, Vue!',showMessage: false,updatedMessage: ''},methods: {changeMessage() {this.message = 'Updated Message';// 使用 nextTick 确保 DOM 已经更新this.$nextTick(() => {this.updatedMessage = this.$el.querySelector('p').textContent;this.showMessage = true;});}}
});
</script></body>
</html>

在这个例子中,当点击按钮改变message数据时,我们使用this.$nextTick来确保在DOM更新之后获取更新后的文本内容,并在页面上显示。

  • 在Vue生命周期中,如果在created()钩子进行DOM操作,也一定要放在nextTick()的回调函数中。因为在created()钩子函数中,页面的DOM还未渲染,这时候也没办法操作DOM,所以,此时如果想要操作DOM,必须将操作的代码放在nextTick()的回调函数中。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Vue nextTick Example</title>
</head>
<body><div id="app"></div><script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
<script>
new Vue({el: '#app',created() {// 在 created 钩子中进行DOM操作,使用 nextTick 确保 DOM 已经渲染this.$nextTick(() => {const element = document.createElement('p');element.textContent = 'DOM Manipulation in created hook';this.$el.appendChild(element);});}
});
</script></body>
</html>

在这个例子中,我们在Vue实例的created钩子中创建一个<p>元素,并使用this.$nextTick确保DOM已经渲染,然后将这个元素添加到页面中。

源码附件:点此下载

这篇关于【揭秘Vue】nextTick的神秘面纱:原理与作用一览无余!的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/396886

相关文章

Spring Security 单点登录与自动登录机制的实现原理

《SpringSecurity单点登录与自动登录机制的实现原理》本文探讨SpringSecurity实现单点登录(SSO)与自动登录机制,涵盖JWT跨系统认证、RememberMe持久化Token... 目录一、核心概念解析1.1 单点登录(SSO)1.2 自动登录(Remember Me)二、代码分析三、

C++中detach的作用、使用场景及注意事项

《C++中detach的作用、使用场景及注意事项》关于C++中的detach,它主要涉及多线程编程中的线程管理,理解detach的作用、使用场景以及注意事项,对于写出高效、安全的多线程程序至关重要,下... 目录一、什么是join()?它的作用是什么?类比一下:二、join()的作用总结三、join()怎么

在MySQL中实现冷热数据分离的方法及使用场景底层原理解析

《在MySQL中实现冷热数据分离的方法及使用场景底层原理解析》MySQL冷热数据分离通过分表/分区策略、数据归档和索引优化,将频繁访问的热数据与冷数据分开存储,提升查询效率并降低存储成本,适用于高并发... 目录实现冷热数据分离1. 分表策略2. 使用分区表3. 数据归档与迁移在mysql中实现冷热数据分

从入门到精通详解LangChain加载HTML内容的全攻略

《从入门到精通详解LangChain加载HTML内容的全攻略》这篇文章主要为大家详细介绍了如何用LangChain优雅地处理HTML内容,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录引言:当大语言模型遇见html一、HTML加载器为什么需要专门的HTML加载器核心加载器对比表二

java中反射Reflection的4个作用详解

《java中反射Reflection的4个作用详解》反射Reflection是Java等编程语言中的一个重要特性,它允许程序在运行时进行自我检查和对内部成员(如字段、方法、类等)的操作,本文将详细介绍... 目录作用1、在运行时判断任意一个对象所属的类作用2、在运行时构造任意一个类的对象作用3、在运行时判断

从原理到实战深入理解Java 断言assert

《从原理到实战深入理解Java断言assert》本文深入解析Java断言机制,涵盖语法、工作原理、启用方式及与异常的区别,推荐用于开发阶段的条件检查与状态验证,并强调生产环境应使用参数验证工具类替代... 目录深入理解 Java 断言(assert):从原理到实战引言:为什么需要断言?一、断言基础1.1 语

python常用的正则表达式及作用

《python常用的正则表达式及作用》正则表达式是处理字符串的强大工具,Python通过re模块提供正则表达式支持,本文给大家介绍python常用的正则表达式及作用详解,感兴趣的朋友跟随小编一起看看吧... 目录python常用正则表达式及作用基本匹配模式常用正则表达式示例常用量词边界匹配分组和捕获常用re

MySQL中的表连接原理分析

《MySQL中的表连接原理分析》:本文主要介绍MySQL中的表连接原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、环境3、表连接原理【1】驱动表和被驱动表【2】内连接【3】外连接【4编程】嵌套循环连接【5】join buffer4、总结1、背景

深度解析Spring AOP @Aspect 原理、实战与最佳实践教程

《深度解析SpringAOP@Aspect原理、实战与最佳实践教程》文章系统讲解了SpringAOP核心概念、实现方式及原理,涵盖横切关注点分离、代理机制(JDK/CGLIB)、切入点类型、性能... 目录1. @ASPect 核心概念1.1 AOP 编程范式1.2 @Aspect 关键特性2. 完整代码实

Java 继承和多态的作用及好处

《Java继承和多态的作用及好处》文章讲解Java继承与多态的概念、语法及应用,继承通过extends复用父类成员,减少冗余;多态实现方法重写与向上转型,提升灵活性与代码复用性,动态绑定降低圈复杂度... 目录1. 继承1.1 什么是继承1.2 继承的作用和好处1.3 继承的语法1.4 子类访问父类里面的成