vue源码分析之nextTick源码分析-逐行逐析-错误分析

nextTick的使用背景

  • 在vue项目中,经常会使用到nextTick这个api,一直在猜想其是怎么实现的,今天有幸研读了下,虽然源码又些许问题,但仍值得借鉴

核心源码解析

判断当前环境使用最合适的API并保存函数

promise

  • 判断是否支持promise,如果支持就使用Promise对象的then方法包裹要执行的 flushCallbacks函数

MutationObserver

  • 判断是否支持MutationObserver,如果支持就创建一个MutationObserver 用于监听dom改动之后执行 flushCallbacks 函数 并赋值给 observer

setImmediate

  • 判断是否支持setImmediate,如果支持就使用setImmediate包裹 flushCallbacks函数

setTimeout

  • 如果以上三种都不支持使用setTimeout包裹 flushCallbacks函数
export let isUsingMicroTask = false//是否使用微任务标志const callbacks = []//任务对列 调用nextTick时传入的回调函数组成的数组
let pending = false//初始化 是否在进行中状态  默认是false  function flushCallbacks() {//循环执行 callbacks  任务队列中的任务pending = falseconst copies = callbacks.slice(0)//callbacks.length = 0for (let i = 0; i < copies.length; i++) {// 依次执行callbacks数组中的函数copies[i]()}
}let timerFuncif (typeof Promise !== 'undefined' && isNative(Promise)) {// 向下兼容操作 如果支持Promise 则使用Promiseconst p = Promise.resolve()//直接返回一个resolved状态的Promise对象timerFunc = () => {p.then(flushCallbacks)// 在Promise的then方法中执行 flushCallbacks 函数if (isIOS) setTimeout(noop)//ios中在一些异常的webview中,promise结束后任务队列并没有刷新,所以强制执行setTimeout(noop)来刷新任务队列}isUsingMicroTask = true//重置使用微任务标示为true
} else if (!isIE && typeof MutationObserver !== 'undefined' && (isNative(MutationObserver) ||MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
// MutationObserver 属性支持,则使用MutationObserver let counter = 1const observer = new MutationObserver(flushCallbacks)// 创建一个MutationObserver 用于监听dom改动之后执行 flushCallbacks 函数 并赋值给 observerconst textNode = document.createTextNode(String(counter))// 创建一个文本节点observer.observe(textNode, {characterData: true})// 每次执行timeFunc都会让文本节点的内容在0/1之间切换timerFunc = () => {counter = (counter + 1) % 2textNode.data = String(counter)}// 切换之后将新值赋值到那个我们MutationObserver观测的文本节点上去isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
// setImmediate 属性支持,则使用setImmediatetimerFunc = () => {setImmediate(flushCallbacks)}
} else {
// 以上都不支持,则使用 setTimeouttimerFunc = () => {setTimeout(flushCallbacks, 0)}
}

调用异步函数执行回调对列

入参分析

nextTick(cb?: Function, ctx?: Object) {}
  • cb是 传入的回调函数
  • ctx是函数执行的上下文
  • 而者都又是可选参数, 但是有问题 下文有解析

函数执行逻辑

  • 将调用nextTick是传入的执行函数添加到 callbacks中
  • 可使用call和传入的ctx修改传入的执行函数的this指向
export function nextTick(cb?: Function, ctx?: Object) {// cb 是 nextTick 包裹的执行函数// ctx 是函数执行的上下文 // console.log("nextTick000",cb,ctx)let _resolve//伪代码// 向 callbacks 数组中添加一个函数callbacks.push(() => {if (cb) {try {cb.call(ctx)//修改回调函数的this指向 } catch (e) {//  异常捕获 如果cb不是函数 抛出异常handleError(e, ctx, 'nextTick')}}  })// console.log("pendingcallbacks",callbacks,pending)if (!pending) {// console.log("pendingcallback0999",callbacks,pending)pending = truetimerFunc()}
}
  • 这个代码有删减,因为其余代码不会执行是 伪代码 下文有解析

伪代码分析

在这里插入图片描述

  • nexttick的参数中cb不能为可选参数,如果cb参数不传将没有回调函数,nextTick将没有意义,并且ctx将成为第一个参数,由于是形参,ctx将顶替cb但是ctx不是函数类型,就会抛错

依次执行nextTick

  • 循环执行 callbacks 任务队列中的任务
function flushCallbacks() {//循环执行 callbacks  任务队列中的任务pending = falseconst copies = callbacks.slice(0)//callbacks.length = 0for (let i = 0; i < copies.length; i++) {// 依次执行callbacks数组中的函数copies[i]()}
}

源码

/* @flow */
/* globals MutationObserver */import { noop } from 'shared/util'
import { handleError } from './error'
import { isIE, isIOS, isNative } from './env'
// isIE 判段运行环境是否在ie浏览器
// isIOS 判断是否是ios
//isNative  判断函数是否是由JavaScript引擎原生实现的
export let isUsingMicroTask = false//是否使用微任务标志const callbacks = []//任务对列 调用nextTick时传入的回调函数组成的数组
let pending = false//初始化 是否在进行中状态  默认是false  function flushCallbacks() {//循环执行 callbacks  任务队列中的任务pending = falseconst copies = callbacks.slice(0)//callbacks.length = 0for (let i = 0; i < copies.length; i++) {// 依次执行callbacks数组中的函数copies[i]()}
}let timerFuncif (typeof Promise !== 'undefined' && isNative(Promise)) {// 向下兼容操作 如果支持Promise 则使用Promiseconst p = Promise.resolve()//直接返回一个resolved状态的Promise对象timerFunc = () => {p.then(flushCallbacks)// 在Promise的then方法中执行 flushCallbacks 函数if (isIOS) setTimeout(noop)//ios中在一些异常的webview中,promise结束后任务队列并没有刷新,所以强制执行setTimeout(noop)来刷新任务队列}isUsingMicroTask = true//重置使用微任务标示为true
} else if (!isIE && typeof MutationObserver !== 'undefined' && (isNative(MutationObserver) ||MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
// MutationObserver 属性支持,则使用MutationObserver let counter = 1const observer = new MutationObserver(flushCallbacks)// 创建一个MutationObserver 用于监听dom改动之后执行 flushCallbacks 函数 并赋值给 observerconst textNode = document.createTextNode(String(counter))// 创建一个文本节点observer.observe(textNode, {characterData: true})// 每次执行timeFunc都会让文本节点的内容在0/1之间切换timerFunc = () => {counter = (counter + 1) % 2textNode.data = String(counter)}// 切换之后将新值赋值到那个我们MutationObserver观测的文本节点上去isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
// setImmediate 属性支持,则使用setImmediatetimerFunc = () => {setImmediate(flushCallbacks)}
} else {
// 以上都不支持,则使用 setTimeouttimerFunc = () => {setTimeout(flushCallbacks, 0)}
}export function nextTick(cb?: Function, ctx?: Object) {// cb 是 nextTick 包裹的执行函数// ctx 是函数执行的上下文 // console.log("nextTick000",cb,ctx)let _resolve//伪代码// 向 callbacks 数组中添加一个函数callbacks.push(() => {if (cb) {try {cb.call(ctx)//修改回调函数的this指向 } catch (e) {//  异常捕获 如果cb不是函数 抛出异常handleError(e, ctx, 'nextTick')}} else if (_resolve) {// 伪代码console.log('ctx')_resolve(ctx)}})// console.log("pendingcallbacks",callbacks,pending)if (!pending) {// console.log("pendingcallback0999",callbacks,pending)pending = truetimerFunc()}// $flow-disable-lineif (!cb && typeof Promise !== 'undefined') {//判断浏览器是否支持 Promise// cb 没有则抛出异常了不会走到这里// 伪代码return new Promise(resolve => {_resolve = resolve})}
}

个人困惑

  • 本人实在是不太理解下列代码的意义是什么,感觉是没用的,但是不太确定,以下是个人分析

分析

  • 首先将执行上下文this抛出是没什么意义的
  • 其次promise的resolve 单独拿出来在此处有什么作用呢
 let _resolve//伪代码if (!cb && typeof Promise !== 'undefined') {//判断浏览器是否支持 Promise// cb 没有则抛出异常了不会走到这里// 伪代码return new Promise(resolve => {_resolve = resolve})}_resolve(ctx)

致谢

  • 感谢您百忙之中抽时间阅读我写的博客,谢谢你的肯定,也希望对您能有所帮助
  • 如果您有更好的见解请在评论区留言或者私聊我,期待与您的交流

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

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

相关文章

2024年2月17日~2月23日周报

文章目录 一、前言二、DDNet架构学习2.1 数据预处理2.2 网络模型构建 三、基于深度学习地震数据去噪处理3.1 深度学习在地震数据去噪中的研究方向3.2 深度学习地震数据去噪流程3.2.1 数据集准备3.2.2 模型构建3.2.3 训练网络 3.3 基于DnCNN的地震数据去噪实验 四、小结4.1 存在…

Javascript数字精度丢失的问题

一、问题 0.1 0.2 0.3 // false 二、浮点数 “浮点数”是一种表示数字的标准&#xff0c;整数也可以用浮点数的格式来存储 我们也可以理解成&#xff0c;浮点数就是小数 在JavaScript中&#xff0c;现在主流的数值类型是Number&#xff0c;而Number采用的是IEEE754规范中…

uniapp开发微信小程序跳转到另一个小程序中

注意&#xff1a;一开始我的云上务工模块是单独的tabbar界面&#xff0c;但是小程序跳转好像不能直接点击tabbar进行&#xff0c;所以我将这里改成了点击首页中的按钮进行跳转 点击这里进行小程序跳转 目录 基础讲解 uniapp小程序跳转的两个方法 调用说明&#xff08;半屏跳转…

C++力扣题目 647--回文子串 516--最长回文子序列

647. 回文子串 力扣题目链接(opens new window) 给定一个字符串&#xff0c;你的任务是计算这个字符串中有多少个回文子串。 具有不同开始位置或结束位置的子串&#xff0c;即使是由相同的字符组成&#xff0c;也会被视作不同的子串。 示例 1&#xff1a; 输入&#xff1a…

面试系列之《Spark》(持续更新...)

参考文档及示例代码均基于pyspark3.1.2 1.什么是RDD&#xff1f;2.job、stage、task如何划分&#xff1f;3.什么是宽窄依赖&#xff1f;4.spark有哪几种部署模式&#xff1f;5.spark中的算子分为哪些类型&#xff0c;举例说明。6.cache、persist、checkpoint的区别&#xff0c;…

C++模板为什么不能声明和定义分离

首先我们要直到C程序运行需要进行的四个阶段。 预处理->编译->汇编->链接 编译&#xff1a;对语法语义分析&#xff0c;分析无误生成汇编&#xff0c;头文件不参加编译&#xff0c;多个源文件是分开单独编译的。 链接&#xff1a;将多个obj文件链接合成一个&#x…

Sora----打破虚实之间的最后一根枷锁----这扇门的背后是人类文明的晟阳还是最后的余晖

目录 一.Sora出道即巅峰 二.为何说Sora是该领域的巨头 三.Sora无敌的背后究竟有怎样先进的处理技术 1.Spacetime Latent Patches 潜变量时空碎片&#xff0c;建构视觉语言系统 2.扩散模型与Diffusion Transformer&#xff0c;组合成强大的信息提取器 3.DiT应用于潜变量时…

关于在分布式环境中RVN和使用场景的介绍4

简介 在前面的文档中&#xff0c;我们介绍了RVN的概念&#xff0c;通过RVN可以解决的某类问题和使用技巧&#xff0c;以及处理RVN的逻辑的具体实现。在本文中&#xff0c;我们将要介绍关于如何使用RVN解决另一种在分布式系统中常出现的问题。 问题 假设我们创建了一个servic…

pikachu靶场-CSRF

CSRF: 介绍&#xff1a; Cross-site request forgery简称为"CSRF”。 在CSF的攻击场景中攻击者会伪造一个请求&#xff08;这个请求一般是一个链接&#xff09; 然后欺骗目标用户进行点击&#xff0c;用户一旦点击了这个请求&#xff0c;整个攻击也就完成了&#xff0…

VSCode-更改系统默认路径

修改vscode中的默认扩展路径&#xff1a;"%USERPROFILE%\.vscode" 打开目录C:\用户\电脑用户名&#xff0c;将.vscode文件剪切至D:\VSCode文件夹下 用管理员身份打开cmd.exe命令界面输入mklink /D "%USERPROFILE%\.vscode" "D:\VSCode\.vscode\"…

同一个包下 golang run时报undefined

问题描述 今天在运行一个项目&#xff0c;一个包下有两个文件&#xff0c;分别是main.go和route&#xff0c;main函数在main.go文件中&#xff0c;main引用了route.go中的两个函数&#xff0c;SetupRoutes和SetupAdminRoutes go build 编译后&#xff0c;直接运行&#xff0c…

【C++私房菜】面向对象中的简单继承

文章目录 一、 继承基本概念二、派生类对象及派生类向基类的类型转换三、继承中的公有、私有和受保护的访问控制规则四、派生类的作用域五、继承中的静态成员 一、 继承基本概念 通过继承&#xff08;inheritance&#xff09;联系在一起的类构成一种层次关系。通常在层次关系的…

Leetcoder Day17| 二叉树 part06

语言&#xff1a;Java/C 654.最大二叉树 给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下&#xff1a; 二叉树的根是数组中的最大元素。左子树是通过数组中最大值左边部分构造出的最大二叉树。右子树是通过数组中最大值右边部分构造出的最大二叉树。 …

免费搭建个人网盘

免费搭建一个属于个人的网盘。 服务端 详情请参考原网站的服务端下载和安装虚拟磁盘Fuse4Ui可以支持把网盘内容挂载成系统的分区&#xff1b; 挂载工具效果图&#xff1a;应用端应用端的下载 效果图

短剧小程序系统,重塑视频观看体验的科技革命

随着科技的飞速发展&#xff0c;人们对于数字化内容的消费需求也在不断增长。在这个大背景下&#xff0c;短剧小程序作为一种新型的视频观看方式&#xff0c;正逐渐受到大众的青睐。本文将探讨短剧小程序的发展背景、特点以及市场前景&#xff0c;分析其在重塑视频观看体验方面…

如何使用Inno Setup制作Unity构建程序的Windows安装程序

1. 准备 &#xff08;1&#xff09;准备好Unity构建的程序集合 必须包括&#xff1a; Data文件夹&#xff08;xxx_Data&#xff09; Mono文件夹&#xff08;MonoBleedingEdge&#xff09; 打包的应用程序文件&#xff08;xxx.exe&#xff09; Unity播放器dll文件&#xff…

基于springboot+vue的大创管理系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

【selenium】执行 Javascript 脚本 滚动、元素的特殊操作等

某些特殊情况下&#xff0c;使用selenium的api无法操作页面元素&#xff0c;点击、滚动实现的某些功能&#xff0c;可以考虑通过执行js来完成。 为什么不用js写自动化&#xff1f;——selenium第一版是js写的&#xff0c;但js兼容性存在问题&#xff0c;所以引入webdriver 现在…

ad15 PCB3D模型导出到SOLIDWORKS

注意&#xff0c;工程文件目录不能用中文&#xff0c;否则导出的文件会不存在 将这个文件直接拖到 SOLIDWORKS 中 下一步很关键 显示出来了 另存为一个转配体就可以了

海思SD3403,SS928/926,hi3519dv500,hi3516dv500移植yolov7,yolov8(14)

自己挖了一个坑,准备做SS928/SD3403的Yolov8的移植,主要是后台私信太多人在问相关的问题。先别着急去写代码,因为在hi3516dv500下的移植还是比较顺利。之前在hi3519av100和hi3559av100系列时遇到过一些问题,所以没有继续去移植新的算法。 SS928架构乍一看和hi3559av100特别…