首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Vue.nextTick核心原理

Vue.nextTick核心原理

原创
作者头像
yyds2026
发布于 2022-10-18 09:34:33
发布于 2022-10-18 09:34:33
60400
代码可运行
举报
文章被收录于专栏:前端开发面经前端开发面经
运行总次数:0
代码可运行

相信大家在写vue项目的时候,一定会发现一个神奇的api,Vue.nextTick。为什么说它神奇呢,那是因为在你做某些操作不生效时,将操作写在Vue.nextTick内,就神奇的生效了。那这是什么原因呢?

让我们一起来研究一下。

简述

  • vue 实现响应式并不是数据发生变化后 DOM 立即变化,而是按照一定策略异步执行 DOM 更新的
  • vue 在修改数据后,视图不会立刻进行更新,而是要等同一事件循环机制内所有数据变化完成后,再统一进行DOM更新
  • nextTick 可以让我们在下次 DOM 更新循环结束之后执行延迟回调,用于获得更新后的 DOM。

事件循环机制

在讨论Vue.nextTick之前,需要先搞清楚事件循环机制,算是实现的基石了,那我们来看一下。

在浏览器环境中,我们可以将我们的执行任务分为宏任务和微任务,

  • 宏任务: 包括整体代码scriptsetTimeoutsetIntervalsetImmediate、 I/O 操作、UI 渲染
  • 微任务: Promise.thenMuationObserver
在这里插入图片描述
在这里插入图片描述

事件循环的顺序,决定js代码的执行顺序。事件循环如下:

在这里插入图片描述
在这里插入图片描述

用代码解释,浏览器中事件循环的顺序同如下代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
for (macroTask of macroTaskQueue) { 
    // 1. 执行一个宏任务
    handleMacroTask();
    // 2. 执行所有的微任务
    for (microTask of microTaskQueue) { 
        handleMicroTask(microTask);
    }
 }

vue数据驱动视图的处理(异步变化DOM)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<template>
  <div>
    <div>{{count}}</div>
    <div @click="handleClick">click</div>
  </div>
</template>
export default {
    data () {
        return {
            number: 0
        };
    },
    methods: {
        handleClick () {
            for(let i = 0; i < 10000; i++) {
                this.count++;
            }
        }
    }
}

分析上述代码:

  • 当点击按钮时,count会被循环改变10000次。那么每次count+1,都会触发count的setter方法,然后修改真实DOM。按此逻辑,这整个过程,DOM会被更新10000次,我们都知道DOM的操作是非常昂贵的,而且这样的操作完全没有必要。所以vue内部在派发更新时做了优化
  • 也就是,并不会每次数据改变都触发 watcher 的回调,而是把这些 watcher 先添加到一个队列queueWatcher里,然后在 nextTick 后执行 flushSchedulerQueue处理
  • 当 count 增加 10000 次时,vue内部会先将对应的 Watcher 对象给 push 进一个队列 queue 中去,等下一个 tick 的时候再去执行。并不需要在下一个 tick 的时候执行 10000 个同样的 Watcher 对象去修改界面,而是只需要执行一个 Watcher 对象,使其将界面上的 0 变成 10000 即可

Vue.nextTick原理

由上一节我们知道,Vue中 数据变化 => DOM变化 是异步过程,一旦观察到数据变化,Vue就会开启一个任务队列,然后把在同一个事件循环 (Event loop) 中观察到数据变化的 Watcher(Vue源码中的Wacher类是用来更新Dep类收集到的依赖的)推送进这个队列。

如果这个watcher被触发多次,只会被推送到队列一次。这种缓冲行为可以有效的去掉重复数据造成的不必要的计算和DOM操作。而在下一个事件循环时,Vue会清空队列,并进行必要的DOM更新。

nextTick的作用是为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback),JS是单线程的,拥有事件循环机制,nextTick的实现就是利用了事件循环的宏任务和微任务。

vue中next-tick.js的源码如下

参考vue实战视频讲解:进入学习

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import { noop } from 'shared/util'
import { handleError } from './error'
import { isIE, isIOS, isNative } from './env'

export let isUsingMicroTask = false

// 首先定义一个 callbacks 数组用来存储 nextTick,在下一个 tick 处理这些回调函数之前,
// 所有的 cb 都会被存在这个 callbacks 数组中
const callbacks = []
// pending 是一个标记位,代表一个等待的状态
let pending = false

// 最后执行 flushCallbacks() 方法,遍历callbacks数组,依次执行里边的每个函数
function flushCallbacks () {
  pending = false
  const copies = callbacks.slice(0)
  callbacks.length = 0
  for (let i = 0; i < copies.length; i++) {
    copies[i]()
  }
}

let timerFunc

/*判断采用哪种异步回调方式由于微任务优先级高,首先尝试微任务模拟1.首先尝试使用Promise.then(微任务)2.尝试使用MuationObserver(微任务)回调3.尝试使用 setImmediate(宏任务)回调4.最后尝试使用setTimeout(宏任务)回调*/
if (typeof Promise !== 'undefined' && isNative(Promise)) {
  const p = Promise.resolve()
  timerFunc = () => {
    p.then(flushCallbacks)
    if (isIOS) setTimeout(noop)
  }
  isUsingMicroTask = true
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
  isNative(MutationObserver) ||
  // PhantomJS and iOS 7.x
  MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
  let counter = 1
  const observer = new MutationObserver(flushCallbacks)
  const textNode = document.createTextNode(String(counter))
  observer.observe(textNode, {
    characterData: true
  })
  timerFunc = () => {
    counter = (counter + 1) % 2
    textNode.data = String(counter)
  }
  isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
  timerFunc = () => {
    setImmediate(flushCallbacks)
  }
} else {
  timerFunc = () => {
    setTimeout(flushCallbacks, 0)
  }
}

export function nextTick (cb?: Function, ctx?: Object) {
  let _resolve
  callbacks.push(() => {
    if (cb) {
      try {
        cb.call(ctx)
      } catch (e) {
        handleError(e, ctx, 'nextTick')
      }
    } else if (_resolve) {
      _resolve(ctx)
    }
  })
  if (!pending) {
    pending = true
    timerFunc()
  }
  // $flow-disable-line
  if (!cb && typeof Promise !== 'undefined') {
    return new Promise(resolve => {
      _resolve = resolve
    })
  }
}

目前浏览器平台并没有实现 nextTick 方法,所以 Vue.js 源码中分别用 Promise、setTimeout、setImmediate 等方式在 microtask(或是task)中创建一个事件,目的是在当前调用栈执行完毕以后(不一定立即)才会去执行这个事件。

nextTick的调用方式

  1. 回调函数方式:Vue.nextTick(callback)
  2. Promise方式:Vue.nextTick().then(callback)
  3. 实例方式:vm.$nextTick(callback)

Vue.nextTick的应用

created生命周期中操作DOM

created钩子函数执行的时候DOM 其实并未进行挂载和渲染,此时就是无法操作DOM的,我们将操作DOM的代码中放到nextTick中,等待下一轮事件循环开始,DOM就已经进行挂载好了,而与这个操作对应的就是mounted钩子函数,因为在mounted执行的时候所有的DOM挂载已完成。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
created(){
  vm.$nextTick(() => {  
      //不使用this.$nextTick()方法操作DOM会报错
      this.$refs.test.innerHTML="created中操作了DOM"
  });
}

修改数据,获取DOM值

当我们修改了data里的数据时,并不能立刻通过操作DOM去获取到里面的值

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<template>
  <div class="test">
    <p ref='msg' id="msg">{{msg}}</p>
  </div>
</template>

<script>
export default {  name: 'Test',  data () {    return {      msg:"hello world",    }  },  methods: {    changeMsg() {      this.msg = "hello Vue"  // vue数据改变,改变了DOM里的innerText
      let msgEle = this.$refs.msg.innerText  //后续js对dom的操作
      console.log(msgEle)  // hello world
      // 输出可以看到data里的数据修改后DOM并没有立即更新,后续的DOM不是最新的
            this.$nextTick(() => {        console.log(this.$refs.msg.innerText) // hello Vue
      })      this.$nextTick().then(() => {        console.log(this.$refs.msg.innerText) // hello Vue
      })    },    changeMsg2() {      this.$nextTick(() => {        console.log(this.$refs.msg.innerText) // 1.hello world 
      })      this.msg = "hello Vue" // 2.
      console.log(this.$refs.msg.innerText) // hello world
      this.$nextTick().then(() => {        console.log(this.$refs.msg.innerText) // hello Vue
      })      // nextTick中先添加的先执行,执行1后,才会执行2(Vue操作Dom的异步)
    }  }}
</script>

v-show/v-if由隐藏变为显示

点击按钮显示原本以 v-show=false或v-if 隐藏起来的输入框,并获取焦点或者获得宽高等的场景

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
英语进阶指南:高效学习方法,提升英语水平
这些开源项目集合了英语学习与翻译工具,包括英语进阶指南、多功能翻译工具、面向程序员的英语学习指南和单词记忆软件。它们提供实用方法,覆盖多个学习方面,满足不同需求。无论您是英语初学者还是想进一步提升,这些资源都能助您轻松实现语言学习目标。
小柒
2023/09/24
3730
英语进阶指南:高效学习方法,提升英语水平
谷歌开源!用 js 编写 Shell 脚本! | 开源日报 No.247
vaultwarden 是用 Rust 编写的非官方 Bitwarden 兼容服务器,以前被称为 bitwarden_rs。该项目提供了以下主要功能、关键特性和核心优势:
小柒
2024/04/25
2370
谷歌开源!用 js 编写 Shell 脚本! | 开源日报 No.247
根据图片生成前端代码:人工智能助你释放效能 | 开源日报 No.98
PHP 是一种流行的通用脚本语言,特别适合 Web 开发。快速、灵活和实用,PHP 支持从博客到世界上最受欢迎的网站等各种应用。PHP 遵循 PHP 许可证 v3.01 发布。
小柒
2023/11/30
1.5K0
根据图片生成前端代码:人工智能助你释放效能 | 开源日报 No.98
NocoDB:把任意数据库转换成智能表格 | 开源日报 No.112
Kata Containers 是一个开源项目和社区,致力于构建轻量级虚拟机 (VMs) 的标准实现,使其在性能上表现得像容器一样,并提供了 VMs 的工作负载隔离和安全优势。该项目主要功能包括:
小柒
2023/12/20
2.1K0
NocoDB:把任意数据库转换成智能表格 | 开源日报 No.112
Go 语言数据库迁移工具:支持多种数据库 | 开源日报 No.268
migrate 是一个用 Go 语言编写的数据库迁移工具,可作为命令行工具或库导入使用。它从不同来源读取迁移,并按正确顺序应用到数据库中。 该项目的主要功能和优势包括:
小柒
2024/05/29
2810
Go 语言数据库迁移工具:支持多种数据库 | 开源日报 No.268
程序员延寿指南:科学延寿 20 年 | 开源日报 No.214
HowToLiveLonger 是一个程序员延寿指南项目。 该项目旨在提供关于如何延长寿命的指南,特别是针对程序员群体。该项目包括术语、目标、关键结果、分析、行动和证据等内容,涵盖了各种与健康相关的方面,并提供了相应的科学研究作为支持。 具体功能和优势包括:
小柒
2024/03/18
2220
程序员延寿指南:科学延寿 20 年 | 开源日报 No.214
电话号码信息收集工具:PhoneInfoga | 开源日报 No.137
PhoneInfoga 是一个用于扫描国际电话号码的信息收集框架,它允许用户首先收集基本信息 (如国家、地区、运营商和线路类型),然后使用各种技术来尝试找到 VoIP 提供商或识别所有者。该工具与一系列必须配置的扫描器配合使用,以便发挥其效力。PhoneInfoga 并不自动化所有操作,而只是帮助调查电话号码。
小柒
2024/01/02
5420
电话号码信息收集工具:PhoneInfoga | 开源日报 No.137
开源日报 0821:帮你修复老旧照片
这篇文章总结了几个开源项目的特点和优势。其中包括了 Python 资源列表、金融研究工具、动画精灵程序、游戏和旧照片修复项目等。这些项目提供了丰富的功能和技术支持,用户可以根据自己的需求进行定制和改进。总的来说,这些开源项目为开发者和用户提供了各种有用的工具和资源。
小柒
2023/09/02
3490
开源日报 0821:帮你修复老旧照片
搞定求职难题:工作岗位列表+简历制作工具 | 开源专题 No.75
SimplifyJobs/New-Grad-Positionshttps://github.com/SimplifyJobs/New-Grad-Positions
小柒
2024/05/10
1460
搞定求职难题:工作岗位列表+简历制作工具 | 开源专题 No.75
Web 自动化神器 Playwright:统一 API 操作多种浏览器 | 开源日报 No.113
pypush 是一个最近作者进行的 iMessage 逆向工程的 POC 演示。它目前可以在 Apple ID 上注册为新设备,设置加密密钥,并发送和接收 iMessages!pypush 完全独立于平台,不需要 Mac 或其他苹果设备来使用。
小柒
2023/12/20
4030
Web 自动化神器 Playwright:统一 API 操作多种浏览器 | 开源日报 No.113
考试面试轻松应对:技术人的备考宝库 | 开源专题 No.58
这个项目是一个技术面试手册,提供了免费和精选的技术面试准备材料。它包括最佳实践问题、编码面试的常见问题、如何准备编程面试以及算法小抄等内容。该项目的核心优势和主要功能有:
小柒
2024/01/02
2020
考试面试轻松应对:技术人的备考宝库 | 开源专题 No.58
解放双手:借助 AI 完成浏览器自动化! | 开源日报 No.313
cpython 是 Python 编程语言的官方实现。 该项目的主要功能、关键特性、核心优势包括:
小柒
2024/07/31
6700
解放双手:借助 AI 完成浏览器自动化! | 开源日报 No.313
Notion 的开源替代品:AppFlowy 助你掌控数据隐私 | 开源日报 No.177
AppFlowy.IO 是一个开源项目,它是 Notion 的替代品。该项目旨在提供个人和企业用户自定义数据和功能的能力。
小柒
2024/02/26
9500
Notion 的开源替代品:AppFlowy 助你掌控数据隐私 | 开源日报 No.177
网盘聚合工具:统筹管理所有网盘资源 | 开源日报 No.203
alist 是一个支持多存储的文件列表/WebDAV 程序,使用 Gin 和 Solidjs。该项目的主要功能、关键特性、核心优势包括:
小柒
2024/03/06
4970
网盘聚合工具:统筹管理所有网盘资源 | 开源日报 No.203
自托管应用集大成,内容管理自动化 | 开源专题 No.93
glance 是一个自托管的仪表板,将所有你的订阅内容集中在一个地方。该项目主要功能、关键特性和核心优势包括:
小柒
2024/06/25
2480
自托管应用集大成,内容管理自动化 | 开源专题 No.93
轻松与任何 SQL 数据库集成:Directus 助你无代码开发 | 开源日报 No.69
这个项目是一个 LinkedIn 技能评估答案的存储库。它提供了各种领域和主题的问题和答案,以帮助用户更好地学习新概念并准备相关考试。该项目具有以下核心优势:
小柒
2023/10/31
5290
轻松与任何 SQL 数据库集成:Directus 助你无代码开发 | 开源日报 No.69
涵盖 Java 核心知识的综合指南:JavaGuide | 开源日报 0912
这是一份涵盖大部分 Java 程序员所需要掌握的核心知识库。该项目包含了 Java 基础、集合、IO、并发等方面的内容,并提供了重要知识点详解和源码分析。此外还有计算机基础(操作系统、网络)、数据库(MySQL、Redis)以及常用框架(Spring/SpringBoot)等相关内容。对于想要准备 Java 面试或者加强自己技术能力的程序员来说,JavaGuide 是一个不错的选择。
小柒
2023/09/14
3470
涵盖 Java 核心知识的综合指南:JavaGuide | 开源日报 0912
安卓自动化 APP:轻松关闭任意开屏广告 | 开源日报 No.116
基于无障碍 + 高级选择器 + 订阅规则的自定义屏幕点击 APP,主要功能包括实现跳过任意开屏广告、关闭应用内部弹窗广告以及一些快捷操作,如微信电脑登录自动同意和领取红包等。其核心优势和特点包括:
小柒
2023/12/20
3740
安卓自动化 APP:轻松关闭任意开屏广告 | 开源日报 No.116
AI法律助手与原创奇幻小说生成
DB-GPT是一个实验性的开源项目,利用本地化的GPT大型模型与数据和环境进行交互,旨在提供私密性和安全性的数据处理解决方案。主要功能:
小柒
2023/09/21
3040
AI法律助手与原创奇幻小说生成
多语言构建和测试系统:并行、快速、可扩展 | 开源日报 No.273
Bend 是一个高度并行的高级编程语言。 该项目解决了在 GPU 等大规模并行硬件上运行程序时,无需显式并行注释、线程生成和锁定等低级操作。
小柒
2024/06/11
1340
多语言构建和测试系统:并行、快速、可扩展 | 开源日报 No.273
推荐阅读
英语进阶指南:高效学习方法,提升英语水平
3730
谷歌开源!用 js 编写 Shell 脚本! | 开源日报 No.247
2370
根据图片生成前端代码:人工智能助你释放效能 | 开源日报 No.98
1.5K0
NocoDB:把任意数据库转换成智能表格 | 开源日报 No.112
2.1K0
Go 语言数据库迁移工具:支持多种数据库 | 开源日报 No.268
2810
程序员延寿指南:科学延寿 20 年 | 开源日报 No.214
2220
电话号码信息收集工具:PhoneInfoga | 开源日报 No.137
5420
开源日报 0821:帮你修复老旧照片
3490
搞定求职难题:工作岗位列表+简历制作工具 | 开源专题 No.75
1460
Web 自动化神器 Playwright:统一 API 操作多种浏览器 | 开源日报 No.113
4030
考试面试轻松应对:技术人的备考宝库 | 开源专题 No.58
2020
解放双手:借助 AI 完成浏览器自动化! | 开源日报 No.313
6700
Notion 的开源替代品:AppFlowy 助你掌控数据隐私 | 开源日报 No.177
9500
网盘聚合工具:统筹管理所有网盘资源 | 开源日报 No.203
4970
自托管应用集大成,内容管理自动化 | 开源专题 No.93
2480
轻松与任何 SQL 数据库集成:Directus 助你无代码开发 | 开源日报 No.69
5290
涵盖 Java 核心知识的综合指南:JavaGuide | 开源日报 0912
3470
安卓自动化 APP:轻松关闭任意开屏广告 | 开源日报 No.116
3740
AI法律助手与原创奇幻小说生成
3040
多语言构建和测试系统:并行、快速、可扩展 | 开源日报 No.273
1340
相关推荐
英语进阶指南:高效学习方法,提升英语水平
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验