Vue学习之---nextTick

前言:目前来说,nextTick我们遇到的比较少,至少对我来说是这样的,但是有一些聪明的小朋友早早就注意到这个知识点了。nextTick 是前端开发(尤其是 Vue 生态)中的核心知识点,原理上跟Vue的异步更新有密切关系,对于面试者考察很有区分度,如果能回答的很好,对面试也是很有帮助的,所以我们有必要花费时间来学习一下。

一、nextTick是什么

我们来看看官方的定义:

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

看完是不是有一堆问号?没关系,我们来拆分一下关键词

下次 DOM 更新循环结束之后

延迟回调

更新后的 DOM

1、 下次 DOM 更新循环结束之后

Vue更新DOM是有策略的,不是同步更新,是异步的,批量更新DOM,避免重绘导致的性能损耗(比如连续修改数据时仅触发一次渲染)

如果你学过防抖和节流的话,有没有感觉这里逻辑有点相像,他们都是前端性能优化手段,那么我们顺便就来复习一下吧。

他们有着共同目标:减少高频操作带来的性能损耗。

实现逻辑上的对比:

机制Vue异步更新防抖(Debounce)节流(Throttle)
触发条件数据变化事件触发(如resize)事件触发(如scroll)
执行策略合并同一事件循环内的所有修改延迟执行,仅保留最后一次操作固定间隔执行一次
应用场景虚拟DOM批量渲染搜索框输入联想滚动加载更多

核心区别:

Vue异步更新:

  • 依赖浏览器事件循环

  • 自动完成,开发者无需手动控制

防抖/节流:

  • 需开发者显式编码实现

  • 作用于业务逻辑层而非框架底层

    呼~复习完了,我们进入正题

    这个关键词跟浏览器的事件循环有关,我们来简单回顾一下:

    浏览器事件循环

    JS是一门单线程的语言,因为它运行在浏览器的渲染主线程中,而主线程只有一个。而渲染主线程承担着许多的工作,渲染页面、执行JS都在其中执行。如果使用同步的方式,极有可能导致主线程产生阻塞,从而导致消息队列中的许多其他任务无法得到执行,所以浏览器采用异步的方式来避免。

     console.log('脚本开始') setTimeout(() => console.log('定时器'), 0) Promise.resolve().then(() => {console.log('Promise') setTimeout(() => console.log('嵌套定时器'), 0) })console.log('脚本结束') ​​​
     ​输出:/* 输出顺序:脚本开始脚本结束Promise定时器嵌套定时器*/

    2、延迟回调

    这里的"延迟"不是指setTimeout那样的宏任务延迟,而是指将回调函数推入微任务队列(microtask queue)等待执行。Vue会根据运行环境自动选择最优的实现方式:

    • 优先使用 Promise.then()

      • 如果当前环境支持 Promise,Vue 会优先使用 Promise.then() 来实现微任务。

      • 这是现代浏览器中最高效的方式,因为它直接利用了 JavaScript 的微任务机制。

    • 降级方案:MutationObserver

      • 如果当前环境不支持 Promise,但支持 MutationObserver,Vue 会使用 MutationObserver 作为替代方案。

      • MutationObserver 的回调会被加入微任务队列,虽然它主要用于监听 DOM 变化,但也可以用来实现微任务的效果。

    • 降级方案:setImmediate

      • 如果当前环境不支持 PromiseMutationObserver,但支持 setImmediate,Vue 会使用 setImmediate

      • setImmediate 是 Node.js 和 IE10+ 提供的一种机制,虽然它是宏任务,但它的执行时机比 setTimeout(fn, 0) 更早。

    • 兜底方案:setTimeout(fn, 0)

      • 如果当前环境不支持 PromiseMutationObserversetImmediate,Vue 会使用 setTimeout(fn, 0) 作为兜底方案。

      • 虽然 setTimeout(fn, 0) 是宏任务,但它是最通用的实现方式,确保在当前同步代码执行完毕后执行回调。

    3、更新后的 DOM

    由于Vue的响应式更新是异步的,直接修改数据后立即访问DOM获取的是旧值。通过nextTick可以确保获取到的是最新渲染结果:

     <script setup>import { ref, nextTick } from 'vue';​const num = ref(0);​const changeValue = async () => {num.value = 1; // 更新数据console.log('第一次打印:', num.value); // 打印当前数据console.log('第一次 DOM:', document.querySelector('div').textContent); // 打印当前 DOM 内容​await nextTick(); // 等待 DOM 更新完成​console.log('第二次打印:', num.value); // 打印当前数据console.log('第二次 DOM:', document.querySelector('div').textContent); // 打印更新后的 DOM 内容};​</script><template><div>{{ num }}</div><button @click="changeValue">Change</button></template>​

    打印结果:

  • 第一次打印:1

  • 第一次打印DOM:0

  • 第二次打印:1

  • 第二次打印DOM:1

  • 总的来说,nextTick就是一个工具,用来确保在 Vue 更新完 DOM 之后,再执行某些操作。它特别有用,因为有时候你修改了数据,Vue 需要一点时间来更新 DOM,而 nextTick 可以让你在 DOM 更新完成之后,立即执行你需要的操作。

    二、使用场景

    1. DOM依赖型操作

    • 获取更新后的元素尺寸/位置

    • 基于新DOM初始化第三方库(如图表库)

    <template><div><h1>Vue 3 nextTick 示例</h1><button @click="loadData">Load Data</button></div></template>​<script setup>import { ref, nextTick } from 'vue';​const dataLoaded = ref(false);​function loadData() {// 模拟数据加载dataLoaded.value = true;​// 确保数据已渲染到 DOMnextTick(() => {initChart();});}​function initChart() {console.log('Chart initialized');// 在这里初始化图表}</script>

    2. 组件通信

    父子组件生命周期执行顺序导致的问题:

    <template><div><button @click="showChild">Show Child</button><ChildComponent v-if="isShow" ref="child" /></div></template>​<script setup>import { ref, nextTick } from 'vue';import ChildComponent from './ChildComponent.vue';​const isShow = ref(false);​function showChild() {isShow.value = true; // 显示子组件nextTick(() => {const child = $refs.child; // 获取子组件的引用if (child) {child.doSomething(); // 确保子组件已挂载并调用方法}});}</script>

    3. 特殊交互处理

    如自动聚焦输入框:

     <template><div><button @click="showInputAndFocus">Show Input and Focus</button><input v-if="showInput" ref="input" /></div></template>​<script setup>import { ref, nextTick } from 'vue';​const showInput = ref(false);​function showInputAndFocus() {showInput.value = true; // 显示输入框nextTick(() => {const inputElement = $refs.input; // 获取输入框的引用if (inputElement) {inputElement.focus(); // 聚焦到输入框}});}</script>

    • 同步代码执行完毕后,事件循环开始处理微任务队列。

    • 执行微任务队列中的 Promise 回调,输出 Promise

    • Promise 回调中,setTimeout(() => console.log('嵌套定时器'), 0) 被加入宏任务队列。

    • 微任务队列清空后,事件循环开始处理宏任务队列。

    • 执行第一个宏任务 setTimeout(() => console.log('定时器'), 0),输出 定时器

    • 执行第二个宏任务 setTimeout(() => console.log('嵌套定时器'), 0),输出 嵌套定时器

    • 执行顺序:同步代码 → 微任务队列 → 宏任务队列

    • 同步代码执行:首先执行当前的同步代码。

    • 清空微任务队列:在同步代码执行完毕后,事件循环会立即清空微任务队列,依次执行微任务队列中的所有任务。

    • 执行宏任务:清空微任务队列后,事件循环会从宏任务队列中取出一个任务执行。

    • 重复步骤 2 和 3:每次执行完一个宏任务后,事件循环会再次清空微任务队列,然后继续执行下一个宏任务。

    • 宏任务:通常用于延迟执行、异步操作或与浏览器的 UI 渲染相关。它们的执行时机在事件循环的每次迭代中,每次只执行一个。(setTimeoutsetInterval setImmediate I/O 操作 UI 渲染

    • 微任务:通常用于需要在当前任务执行完毕后立即执行的回调。它们的优先级高于宏任务,会在每次宏任务执行完毕后立即清空。(Promise MutationObserver queueMicrotask)

    • 注意,这里为了方便理解,我们把任务笼统归类为宏任务和微任务,现在对于宏任务和微任务已经有了更细的划分,大家可以去了解一下,但是这里只是为了让大家方便理解,采用了宏任务和微任务的说法,其实也是对的,但是这样说比较旧

三、实战测试

测试案例1:验证执行顺序

猜猜最后打印出什么结果

<template><div><h1>Vue 3 nextTick 示例</h1></div></template>​<script setup>import { onMounted, nextTick } from 'vue';​onMounted(() => {console.log('1');Promise.resolve().then(() => console.log('2'));nextTick(() => console.log('3'));setTimeout(() => console.log('4'), 0);});</script>

打印:1234

首先执行同步操作,打印1

执行微队列的任务promise和nextTick,打印2和3

执行宏队列任务,打印4

测试案例2:连续修改测试

猜猜最后打印出什么结果

 
<template><div><h1>Vue 3 nextTick 示例</h1><button @click="testBatchUpdate">Test Batch Update</button></div></template>​<script setup>import { ref, nextTick } from 'vue';​const count = ref(0);​function testBatchUpdate() {count.value = 1;nextTick(() => console.log(count.value));count.value = 2;nextTick(() => console.log(count.value));}</script>

打印:22

先执行同步操作,先后吧count的值改为1和2,然后执行微队列的任务,打印两次count的值

四、面试常问

整理了一些面视可能会问的问题

1. 核心原理

Q:nextTick的实现原理是什么? A:基于JavaScript事件循环机制,优先使用微任务队列实现异步回调。Vue会维护一个回调队列,在同一个tick中多次调用nextTick只会向队列添加任务,最终通过异步API批量执行。

2. 执行时机

Q:nextTick与setTimeout(fn, 0)的区别? A:nextTick会优先使用微任务(执行早于setTimeout),且能自动选择最优的异步方案。setTimeout属于宏任务,执行时机更晚。

3. 与Vue响应式的关系

Q:为什么Vue选择异步更新DOM? A:主要考虑两点:

  1. 性能优化:合并同一事件循环中的所有数据变更

  2. 逻辑合理性:确保无论修改多少次数据,组件只更新一次

4. 特殊场景

Q:nextTick回调中再修改数据会怎样? A:会进入新的更新周期,可能触发额外的渲染。应避免这种嵌套使用。

Q:为什么 nextTick 回调中修改数据可能导致额外渲染? A:因为 nextTick 回调执行时,当前渲染周期已结束。若在回调中修改数据,会触发新的渲染周期(类似“连锁反应”)。

5. nextTick的常见使用场景有哪些

  • A

    1、确保 DOM 更新完成后执行某些操作(如获取元素尺寸、滚动位置等)。

    2、在子组件挂载后执行某些操作(如调用子组件的方法)。

    3、在批量更新数据后,确保所有更新完成后再执行某些操作。

    6. nextTick如何帮助优化性能

    • A

      • 通过确保在 DOM 更新完成后执行回调,nextTick 可以避免在 DOM 更新过程中进行不必要的操作,从而减少性能开销。

        上面只是一部分,多的大家可以自己下去搜索一些

五、致谢

bilibili 浏览器事件循环原理哔哩哔哩bilibili

官方网站 nextTick | Vue3

腾讯元宝 豆包 kimi

我自己 😎😋

爱学习的你们👏🌹

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

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

相关文章

MS2691 全频段、多模导航、射频低噪声放大器芯片,应用于导航仪 双频测量仪

MS2691 全频段、多模导航、射频低噪声放大器芯片&#xff0c;应用于导航仪 双频测量仪 产品简述 MS2691 是一款具有 1164MHz  1615MHz 全频段、低功耗的低噪声放大器芯片。该芯片通过对外围电路的简单配置&#xff0c;使得频带具有宽带或窄带特性。支持不同频段的各种导…

学习STC51单片机30(芯片为STC89C52RCRC)

每日一言 当你感到疲惫时&#xff0c;正是成长的关键时刻&#xff0c;再坚持一下。 IIC协议 是的&#xff0c;IIC协议就是与我们之前的串口通信协议是同一个性质&#xff0c;就是为了满足模块的通信&#xff0c;其实之前的串口通信协议叫做UART协议&#xff0c;我们千万不要弄…

python打卡day47@浙大疏锦行

昨天代码中注意力热图的部分顺移至今天 知识点回顾&#xff1a; 热力图 作业&#xff1a;对比不同卷积层热图可视化的结果 以下是不同卷积层特征图可视化的对比实现&#xff1a; import torch import matplotlib.pyplot as pltdef compare_conv_layers(model, input_tensor):# …

蓝桥杯单片机之通过实现同一个按键的短按与长按功能

实现按键的短按与长按的不同功能 问题分析 对于按键短按&#xff0c;通常是松开后实现其功能&#xff0c;而不会出现按下就进行后续的操作&#xff1b;而对于按键长按&#xff0c;则不太一样&#xff0c;按键长按可能分为两种情况&#xff0c;一是长按n秒后实现后续功能&…

数据导入技术(文档加载)

1. 简单文本的读取 用LangChain读入txt文档 # 读取单个txt文件 import os from langchain_community.document_loaders import TextLoader # 获取当前脚本文件所在的目录 script_dir os.path.dirname(__file__) print(f"获取当前脚本文件所在的目录&#xff1a;{script…

靶场(二十)---靶场体会小白心得 ---jacko

老样子开局先看端口&#xff0c;先看http端口 PORT STATE SERVICE VERSION 80/tcp open http Microsoft IIS httpd 10.0 |_http-title: H2 Database Engine (redirect) | http-methods: |_ Potentially risky methods: TRACE |_http-server-header:…

让AI看见世界:MCP协议与服务器的工作原理

让AI看见世界&#xff1a;MCP协议与服务器的工作原理 MCP&#xff08;Model Context Protocol&#xff09;是一种创新的通信协议&#xff0c;旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天&#xff0c;MCP正成为连接AI与现实世界的重要桥梁。…

|从零开始的Pyside2界面编程| 用Pyside2打造一个AI助手界面

&#x1f411; |从零开始的Pyside2界面编程| 用Pyside2打造一个AI助手界面 &#x1f411; 文章目录 &#x1f411; |从零开始的Pyside2界面编程| 用Pyside2打造一个AI助手界面 &#x1f411;♈前言♈♈调取Deepseek大模型♈♒准备工作♒♒调用API♒ ♈将模型嵌入到ui界面中♈♈…

如何利用Elastic Stack(ELK)进行安全日志分析

在以下文章中&#xff0c;我将解释如何使用Elastic Stack&#xff08;ELK&#xff09;进行安全日志分析&#xff0c;以提高安全性和监控网络活动。ELK是一个功能强大的开源日志管理和分析平台&#xff0c;由Elasticsearch、Logstash和Kibana组成&#xff0c;适用于各种用例&…

网络安全-等级保护(等保)3-0 等级保护测评要求现行技术标准

################################################################################ 第三章&#xff1a;测评要求、测评机构要求&#xff0c;最终目的是通过测评&#xff0c;所以我们将等保要求和测评相关要求一一对应形成表格。 GB/T 28448-2019 《信息安全技术 网络安全等…

网络通讯知识——通讯分层介绍,gRPC,RabbitMQ分层

网络通讯分层 网络通讯分层是为了将复杂的网络通信问题分解为多个独立、可管理的层次&#xff0c;每个层次专注于特定功能。目前主流的分层模型包括OSI七层模型和TCP/IP四层&#xff08;或五层&#xff09;模型&#xff0c;以下是详细解析&#xff1a; 一、OSI七层模型&#…

gopool 源码分析

gopool gopool是字节跳动开源节流的gopkg包中协程池的一个实现。 关键结构 协程池&#xff1a; type pool struct {// The name of the poolname string// capacity of the pool, the maximum number of goroutines that are actually working// 协程池的最大容量cap int32…

【工作记录】接口功能测试总结

如何对1个接口进行接口测试 一、单接口功能测试 1、接口文档信息 理解接口文档的内容&#xff1a; 请求URL: https://[ip]:[port]/xxxserviceValidation 请求方法: POST 请求参数: serviceCode(必填), servicePsw(必填) 响应参数: status, token 2、编写测试用例 2.1 正…

Linux中su与sudo命令的区别:权限管理的关键差异解析

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐&#xff1a;「storms…

乐观锁与悲观锁的实现和应用

乐观锁与悲观锁&#xff1a;原理、实现与应用详解 在并发编程和数据库操作中&#xff0c;乐观锁和悲观锁是两种重要的并发控制策略&#xff0c;它们在原理、实现方式和应用场景上存在显著差异。下面我们将通过图文结合的方式&#xff0c;深入探讨这两种锁机制。 一、基本概念 1…

ios苹果系统,js 滑动屏幕、锚定无效

现象&#xff1a;window.addEventListener监听touch无效&#xff0c;划不动屏幕&#xff0c;但是代码逻辑都有执行到。 scrollIntoView也无效。 原因&#xff1a;这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作&#xff0c;从而会影响…

Redis主从复制原理二 之 主从复制工作流程

概述 本文紧接「Redis主从复制的原理一 之 概述」&#xff0c;详细介绍了Redis的主从服务过程及原理。 主从复制工作流程 主从复制过程大体可以分为3个阶段&#xff1a; 建立连接阶段&#xff08;即准备阶段&#xff09;数据同步阶段命令传播阶段 阶段一&#xff1a;建立连接阶…

Markdown基础(1.2w字)

1. Markdown基础 这次就没目录了&#xff0c;因为md格式太乱了写示例&#xff0c;展示那些都太乱了&#xff0c;导致目录很乱。 &#xff08;我是XX&#xff0c;出现了很多错误&#xff0c;有错误和我说&#xff09; 1.1 Markdown简介 Markdown是一种轻量级的标记语言&#…

JAVA-springboot log日志

SpringBoot从入门到精通-第8章 日志的操作 一、Spring Boot默认的日志框架 SpringBoot支持很多种日志框架&#xff0c;通常情况下&#xff0c;这些日志框架都是由一个日志抽象层和一个日志实现层搭建而成的&#xff0c;日志抽象层是为记录日志提供的一套标准且规范的框架&…

Vue 渲染 Markdown 文件完全指南

前言 大家好&#xff0c;我是一诺&#xff0c;今天分享的是vue中渲染markdown文件。这是一个常见的需求&#xff0c;比如用户隐私协议页、技术说明等文档页面~ 本文将详细介绍如何在 Vue 中渲染 Markdown 文件&#xff0c;并美化代码块的显示效果。 基础概念 什么是 Markdo…