$nextTick底层原理(详细) - vue篇

2024-03-06 04:20

本文主要是介绍$nextTick底层原理(详细) - vue篇,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

公众号:需要以下pdf,关注下方


2023已经过完了,让我们来把今年的面试题统计号,来备战明年的金三银四!所以,不管你是社招还是校招,下面这份前端面试工程师高频面试题,请收好。

前言

nextTick 是 Vue 的一个核心实现,$nextTick方法将回调延迟到下次DOM更新循环之后执行。Vue 的 nextTick 其本质是对 JavaScript 执行原理 EventLoop 的一种应用。

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

前置知识 - js事件循环

JS 运行机制

JS 执行是单线程的,基于事件循环。事件循环大致分为以下步骤:

  1. 所有同步任务都在主线程上执行,形成一个执行栈。
  2. 异步任务放进任务队列,异步任务分为宏任务和微任务
  3. 执行栈所有同步任务执行完成,就会执行任务队列。对应的异步任务,结束等待状态,进入执行栈,开始执行。
  4. 主线程不断重复上面的第三步。

主线程的执行过程就是一个 tick,而所有的异步结果都是通过 “任务队列” 来调度。 消息队列中存放的是一个个的任务(task)。 task 分为两大类,分别是 macro task 和 micro task,并且每个 macro task(宏任务) 结束后,都要清空所有的 micro task(微任务)。

如图所示

 
for (macroTask of macroTaskQueue) {// 执行宏任务handleMacroTask();// 执行所有微任务for (microTask of microTaskQueue) {handleMicroTask(microTask);}
}

宏任务: script、setTimeout、setInterval、Node中的setImmediate 等
微任务: Promise.then、MutationObserver、Node 中的 Process.nextTick等

nextTick的具体实现原理

上源码

源码分为两部分,一是判断当前环境能使用的 API 并保存异步函数,二是调用异步函数执行回调队列

timerFunc函数定义,根据当前环境支持什么方法则确定调用哪个,分别有:

 
Promise.then`、`MutationObserver`、`setImmediate`、`setTimeout

通过上面任意一种方法,进行降级操作

 
export let isUsingMicroTask = false 
const callbacks = [] // 回调队列
let pending = false// 该方法执行队列中的全部回调
function flushCallbacks () {pending = falseconst copies = callbacks.slice(0)callbacks.length = 0// 执行任务队列for (let i = 0; i < copies.length; i++) {copies[i]()}
}
let timerFunc // 用来保存调用异步任务方法
// 判断1:是否原生支持Promise
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) ||MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {// 判断2:是否原生支持MutationObserverlet counter = 1const observer = new MutationObserver(flushCallbacks)const textNode = document.createTextNode(String(counter))observer.observe(textNode, {characterData: true})timerFunc = () => {counter = (counter + 1) % 2textNode.data = String(counter)}isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {//判断3:是否原生支持setImmediattimerFunc = () => {setImmediate(flushCallbacks)}
} else {//判断4:上面都不行,直接用setTimeouttimerFunc = () => {setTimeout(flushCallbacks, 0)}
}
 
export function nextTick(cb?: Function, ctx?: Object) {let _resolve;
​// cb 回调函数会经统一处理压入 callbacks 数组callbacks.push(() => {if (cb) {try {cb.call(ctx);} catch (e) {handleError(e, ctx, 'nextTick');}} else if (_resolve) {_resolve(ctx);}});
​// 执行异步延迟函数 timerFuncif (!pending) {pending = true;timerFunc();}
​// 当 nextTick 没有传入函数参数的时候,返回一个 Promise 化的调用if (!cb && typeof Promise !== 'undefined') {return new Promise(resolve => {_resolve = resolve;});}
}

callbacks就是异步操作队列

callbacks新增回调函数后又执行了timerFunc函数,pending是用来标识同一个时间只能执行一次

 
function flushCallbacks () {pending = falseconst copies = callbacks.slice(0)callbacks.length = 0for (let i = 0; i < copies.length; i++) {copies[i]()}
}

把回调函数放入callbacks等待执行,将执行函数放到微任务或者宏任务中 无论是微任务还是宏任务,都会放到flushCallbacks使用,这里将callbacks里面的函数复制一份,同时callbacks置空

循环遍历执行callbacks里面的函数

总结

  1. Vue 的 nextTick 其本质是对 JavaScript 执行原理 EventLoop 的一种应用
  2. nextTick核心是利用了如 Promise 、MutationObserver、setImmediate、setTimeout的原生 JavaScript 方法来模拟对应的微/宏任务的实现,根据当前环境支持什么方法则确定调用哪个


原文链接:https://juejin.cn/post/7314493016497684520
 

这篇关于$nextTick底层原理(详细) - vue篇的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

部署Vue项目到服务器后404错误的原因及解决方案

《部署Vue项目到服务器后404错误的原因及解决方案》文章介绍了Vue项目部署步骤以及404错误的解决方案,部署步骤包括构建项目、上传文件、配置Web服务器、重启Nginx和访问域名,404错误通常是... 目录一、vue项目部署步骤二、404错误原因及解决方案错误场景原因分析解决方案一、Vue项目部署步骤

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

Spring AI集成DeepSeek的详细步骤

《SpringAI集成DeepSeek的详细步骤》DeepSeek作为一款卓越的国产AI模型,越来越多的公司考虑在自己的应用中集成,对于Java应用来说,我们可以借助SpringAI集成DeepSe... 目录DeepSeek 介绍Spring AI 是什么?1、环境准备2、构建项目2.1、pom依赖2.2

前端原生js实现拖拽排课效果实例

《前端原生js实现拖拽排课效果实例》:本文主要介绍如何实现一个简单的课程表拖拽功能,通过HTML、CSS和JavaScript的配合,我们实现了课程项的拖拽、放置和显示功能,文中通过实例代码介绍的... 目录1. 效果展示2. 效果分析2.1 关键点2.2 实现方法3. 代码实现3.1 html部分3.2

Goland debug失效详细解决步骤(合集)

《Golanddebug失效详细解决步骤(合集)》今天用Goland开发时,打断点,以debug方式运行,发现程序并没有断住,程序跳过了断点,直接运行结束,网上搜寻了大量文章,最后得以解决,特此在这... 目录Bug:Goland debug失效详细解决步骤【合集】情况一:Go或Goland架构不对情况二:

Python itertools中accumulate函数用法及使用运用详细讲解

《Pythonitertools中accumulate函数用法及使用运用详细讲解》:本文主要介绍Python的itertools库中的accumulate函数,该函数可以计算累积和或通过指定函数... 目录1.1前言:1.2定义:1.3衍生用法:1.3Leetcode的实际运用:总结 1.1前言:本文将详

Deepseek R1模型本地化部署+API接口调用详细教程(释放AI生产力)

《DeepseekR1模型本地化部署+API接口调用详细教程(释放AI生产力)》本文介绍了本地部署DeepSeekR1模型和通过API调用将其集成到VSCode中的过程,作者详细步骤展示了如何下载和... 目录前言一、deepseek R1模型与chatGPT o1系列模型对比二、本地部署步骤1.安装oll

Spring Boot整合log4j2日志配置的详细教程

《SpringBoot整合log4j2日志配置的详细教程》:本文主要介绍SpringBoot项目中整合Log4j2日志框架的步骤和配置,包括常用日志框架的比较、配置参数介绍、Log4j2配置详解... 目录前言一、常用日志框架二、配置参数介绍1. 日志级别2. 输出形式3. 日志格式3.1 PatternL

Springboot 中使用Sentinel的详细步骤

《Springboot中使用Sentinel的详细步骤》文章介绍了如何在SpringBoot中使用Sentinel进行限流和熔断降级,首先添加依赖,配置Sentinel控制台地址,定义受保护的资源,... 目录步骤 1: 添加 Sentinel 依赖步骤 2: 配置 Sentinel步骤 3: 定义受保护的

CSS弹性布局常用设置方式

《CSS弹性布局常用设置方式》文章总结了CSS布局与样式的常用属性和技巧,包括视口单位、弹性盒子布局、浮动元素、背景和边框样式、文本和阴影效果、溢出隐藏、定位以及背景渐变等,通过这些技巧,可以实现复杂... 一、单位元素vm 1vm 为视口的1%vh 视口高的1%vmin 参照长边vmax 参照长边re