0

0

Vue中nextTick有什么功能?nextTick的简单实现

不言

不言

发布时间:2018-11-24 14:31:40

|

6304人浏览过

|

来源于博客园

转载

本篇文章给大家带来的内容是关于Vue中nextTick有什么功能?如何实现?,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

nextTick是Vue的一个核心功能,在Vue内部实现中也经常用到nextTick。但是,很多新手不理解nextTick的原理,甚至不清楚nextTick的作用。

那么,我们就先来看看nextTick是什么。

nextTick功能

看看官方文档的描述:

立即学习前端免费学习笔记(深入)”;

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

再看看官方示例:

// 修改数据
vm.msg = 'Hello'
// DOM 还没有更新
Vue.nextTick(function () {
  // DOM 更新了
})

// 作为一个 Promise 使用 (2.1.0 起新增,详见接下来的提示)
Vue.nextTick()
  .then(function () {
    // DOM 更新了
  })

2.1.0 起新增:如果没有提供回调且在支持 Promise 的环境中,则返回一个 Promise。请注意 Vue 不自带 Promise 的 polyfill,所以如果你的目标浏览器不原生支持 Promise (IE:你们都看我干嘛),你得自己提供 polyfill。

可以看到,nextTick主要功能就是改变数据后让回调函数作用于dom更新后。很多人一看到这里就懵逼了,为什么需要在dom更新后再执行回调函数,我修改了数据后,不是dom自动就更新了吗?

这个和JS中的Event Loop有关,网上教程不计其数,在此就不再赘述了。建议明白Event Loop后再继续向下阅读本文。

举个实际的例子:

我们有个带有分页器的表格,每次翻页需要选中第一项。正常情况下,我们想的是点击翻页器,向后台获取数据,更新表格数据,操纵表格API选中第一项。

但是,你会发现,表格数据是更新了,但是并没有选中第一项。因为,你选中第一项时,虽然数据更新了,但是DOM并没有更新。此时,你可以使用nextTick,在DOM更新后再操纵表格第一项的选中。

那么,nextTick到底做了什么了才能实现在DOM更新后执行回调函数?

源码分析

nextTick的源码位于src/core/util/next-tick.js,总计118行,十分的短小精悍,十分适合初次阅读源码的同学。

nextTick源码主要分为两块:

1.能力检测

2.根据能力检测以不同方式执行回调队列

度加创作工具
度加创作工具

百度出品的、人人可用的AIGC创作平台

下载

能力检测

这一块其实很简单,众所周知,Event Loop分为宏任务(macro task)以及微任务( micro task),不管执行宏任务还是微任务,完成后都会进入下一个tick,并在两个tick之间执行UI渲染。

但是,宏任务耗费的时间是大于微任务的,所以在浏览器支持的情况下,优先使用微任务。如果浏览器不支持微任务,使用宏任务;但是,各种宏任务之间也有效率的不同,需要根据浏览器的支持情况,使用不同的宏任务。

nextTick在能力检测这一块,就是遵循的这种思想。

// Determine (macro) task defer implementation.
// Technically setImmediate should be the ideal choice, but it's only available
// in IE. The only polyfill that consistently queues the callback after all DOM
// events triggered in the same loop is by using MessageChannel.
/* istanbul ignore if */
// 如果浏览器不支持Promise,使用宏任务来执行nextTick回调函数队列
// 能力检测,测试浏览器是否支持原生的setImmediate(setImmediate只在IE中有效)
if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
  // 如果支持,宏任务( macro task)使用setImmediate
  macroTimerFunc = () => {
    setImmediate(flushCallbacks)
  }
  // 同上
} else if (typeof MessageChannel !== 'undefined' && (
  isNative(MessageChannel) ||
  // PhantomJS
  MessageChannel.toString() === '[object MessageChannelConstructor]'
)) {
  const channel = new MessageChannel()
  const port = channel.port2
  channel.port1.onmessage = flushCallbacks
  macroTimerFunc = () => {
    port.postMessage(1)
  }
} else {
  /* istanbul ignore next */
  // 都不支持的情况下,使用setTimeout
  macroTimerFunc = () => {
    setTimeout(flushCallbacks, 0)
  }
}

首先,检测浏览器是否支持setImmediate,不支持就使用MessageChannel,再不支持只能使用效率最差但是兼容性最好的setTimeout了。

之后,检测浏览器是否支持Promise,如果支持,则使用Promise来执行回调函数队列,毕竟微任务速度大于宏任务。如果不支持的话,就只能使用宏任务来执行回调函数队列。

执行回调函数队列

执行回调函数队列的代码刚好在一头一尾

// 回调函数队列
const callbacks = []
// 异步锁
let pending = false

// 执行回调函数
function flushCallbacks () {
  // 重置异步锁
  pending = false
  // 防止出现nextTick中包含nextTick时出现问题,在执行回调函数队列前,提前复制备份,清空回调函数队列
  const copies = callbacks.slice(0)
  callbacks.length = 0
  // 执行回调函数队列
  for (let i = 0; i < copies.length; i++) {
    copies[i]()
  }
}

...

// 我们调用的nextTick函数
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
    if (useMacroTask) {
      macroTimerFunc()
    } else {
      microTimerFunc()
    }
  }
  // $flow-disable-line
  // 2.1.0新增,如果没有提供回调,并且支持Promise,返回一个Promise
  if (!cb && typeof Promise !== 'undefined') {
    return new Promise(resolve => {
      _resolve = resolve
    })
  }
}

总体流程就是,接收回调函数,将回调函数推入回调函数队列中。

同时,在接收第一个回调函数时,执行能力检测中对应的异步方法(异步方法中调用了回调函数队列)。

如何保证只在接收第一个回调函数时执行异步方法?

nextTick源码中使用了一个异步锁的概念,即接收第一个回调函数时,先关上锁,执行异步方法。此时,浏览器处于等待执行完同步代码就执行异步代码的情况。

打个比喻:相当于一群旅客准备上车,当第一个旅客上车的时候,车开始发动,准备出发,等到所有旅客都上车后,就可以正式开车了。

当然执行flushCallbacks函数时有个难以理解的点,即:为什么需要备份回调函数队列?执行的也是备份的回调函数队列?

因为,会出现这么一种情况:nextTick套用nextTick。如果flushCallbacks不做特殊处理,直接循环执行回调函数,会导致里面nextTick中的回调函数会进入回调队列。这就相当于,下一个班车的旅客上了上一个班车。

实现一个简易的nextTick

说了这么多,我们来实现一个简单的nextTick:

let callbacks = []
let pending = false

function nextTick (cb) {
    callbacks.push(cb)

    if (!pending) {
        pending = true
        setTimeout(flushCallback, 0)
    }
}

function flushCallback () {
    pending = false
    let copies = callbacks.slice()
    callbacks.length = 0
    copies.forEach(copy => {
        copy()
    })
}

可以看到,在简易版的nextTick中,通过nextTick接收回调函数,通过setTimeout来异步执行回调函数。通过这种方式,可以实现在下一个tick中执行回调函数,即在UI重新渲染后执行回调函数。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
bootstrap安装教程
bootstrap安装教程

本专题整合了bootstrap安装相关教程,阅读专题下面的文章了解更多详细操作教程。

3

2026.03.18

bootstrap框架介绍
bootstrap框架介绍

本专题整合了bootstrap框架相关介绍,阅读专题下面的文章了解更多详细内容。

4

2026.03.18

vscode 格式化
vscode 格式化

本专题整合了vscode格式化相关内容,阅读专题下面的文章了解更多详细内容。

2

2026.03.18

vscode设置中文教程
vscode设置中文教程

本专题整合了vscode设置中文相关内容,阅读专题下面的文章了解更多详细教程。

0

2026.03.18

vscode更新教程合集
vscode更新教程合集

本专题整合了vscode更新相关内容,阅读专题下面的文章了解更多详细教程。

3

2026.03.18

Gemini网页版零基础入门:5分钟上手Gemini聊天指南
Gemini网页版零基础入门:5分钟上手Gemini聊天指南

本专题专为零基础用户打造,5分钟快速掌握Gemini网页版核心用法。从账号登录到界面布局,详解如何发起对话、优化提示词及利用多模态功能。通过实战案例,教你高效获取信息、创作内容与分析数据。无论学习还是工作,轻松开启AI辅助新时代,让Gemini成为你的得力智能助手。

9

2026.03.18

Python WebSocket实时通信与异步服务开发实践
Python WebSocket实时通信与异步服务开发实践

本专题聚焦 Python 在实时通信场景中的开发实践,系统讲解 WebSocket 协议原理、长连接管理、消息推送机制以及异步服务架构设计。内容包括客户端与服务端通信实现、连接稳定性优化、消息队列集成及高并发处理策略。通过完整案例,帮助开发者构建高效稳定的实时通信系统,适用于聊天应用、实时数据推送等场景。

8

2026.03.18

Java Spring Security权限控制与认证机制实战
Java Spring Security权限控制与认证机制实战

本专题围绕 Java 后端安全体系建设展开,重点讲解 Spring Security 在权限控制与认证机制中的应用实践。内容涵盖用户认证流程、权限模型设计、JWT 鉴权方案、OAuth2 集成以及接口安全防护策略。通过实际项目案例,帮助开发者构建安全可靠的后端认证体系,提升系统安全性与可扩展能力。

26

2026.03.18

抖漫入口地址合集
抖漫入口地址合集

本专题整合了抖漫入口地址相关合集,阅读专题下面的文章了解更多详细地址。

176

2026.03.17

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号