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

news2024/11/6 8:05:41

nextTick的使用背景

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

核心源码解析

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

promise

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

MutationObserver

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

setImmediate

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

setTimeout

  • 如果以上三种都不支持使用setImmediate包裹 flushCallbacks函数
export let isUsingMicroTask = false//是否使用微任务标志

const callbacks = []//任务对列 调用nextTick时传入的回调函数组成的数组
let pending = false//初始化 是否在进行中状态  默认是false  

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

let timerFunc

if (typeof Promise !== 'undefined' && isNative(Promise)) {
  // 向下兼容操作 如果支持Promise 则使用Promise
  const 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 = 1
  const observer = new MutationObserver(flushCallbacks)
  // 创建一个MutationObserver 用于监听dom改动之后执行 flushCallbacks 函数 并赋值给 observer
  const textNode = document.createTextNode(String(counter))
  // 创建一个文本节点
  observer.observe(textNode, {
    characterData: true
  })
  // 每次执行timeFunc都会让文本节点的内容在0/1之间切换
  timerFunc = () => {
    counter = (counter + 1) % 2
    textNode.data = String(counter)
  }
  // 切换之后将新值赋值到那个我们MutationObserver观测的文本节点上去
  isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
// setImmediate 属性支持,则使用setImmediate
  timerFunc = () => {
    setImmediate(flushCallbacks)
  }
} else {
// 以上都不支持,则使用 setTimeout
  timerFunc = () => {
    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 = true
    timerFunc()
  }
}
  • 这个代码有删减,因为其余代码不会执行是 伪代码 下文有解析

伪代码分析

在这里插入图片描述

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

依次执行nextTick

  • 循环执行 callbacks 任务队列中的任务
function flushCallbacks() {
  //循环执行 callbacks  任务队列中的任务
  pending = false
  const copies = callbacks.slice(0)//
  callbacks.length = 0
  for (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 = false
  const copies = callbacks.slice(0)//
  callbacks.length = 0
  for (let i = 0; i < copies.length; i++) {
    // 依次执行callbacks数组中的函数
    copies[i]()
  }
}

let timerFunc

if (typeof Promise !== 'undefined' && isNative(Promise)) {
  // 向下兼容操作 如果支持Promise 则使用Promise
  const 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 = 1
  const observer = new MutationObserver(flushCallbacks)
  // 创建一个MutationObserver 用于监听dom改动之后执行 flushCallbacks 函数 并赋值给 observer
  const textNode = document.createTextNode(String(counter))
  // 创建一个文本节点
  observer.observe(textNode, {
    characterData: true
  })
  // 每次执行timeFunc都会让文本节点的内容在0/1之间切换
  timerFunc = () => {
    counter = (counter + 1) % 2
    textNode.data = String(counter)
  }
  // 切换之后将新值赋值到那个我们MutationObserver观测的文本节点上去
  isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
// setImmediate 属性支持,则使用setImmediate
  timerFunc = () => {
    setImmediate(flushCallbacks)
  }
} else {
// 以上都不支持,则使用 setTimeout
  timerFunc = () => {
    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 = true
    timerFunc()
  }
  // $flow-disable-line
  if (!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.coloradmin.cn/o/1463425.html

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

相关文章

【RL】Actor-Critic Methods

Lecture 10: Actor-Critic Methods The simplest actor-critic (QAC) 回顾 policy 梯度的概念&#xff1a; 1、标量指标 J ( θ ) J(\theta) J(θ)&#xff0c;可以是 v ˉ π \bar{v}_{\pi} vˉπ​ 或 r ˉ π \bar{r}_{\pi} rˉπ​。 2、最大化 J ( θ ) J(\theta)…

计算机服务器中了DevicData勒索病毒怎么办?DevicData勒索病毒解密数据恢复

网络技术的发展与更新为企业提供了极大便利&#xff0c;让越来越多的企业走向了正规化、数字化&#xff0c;因此&#xff0c;企业的数据安全也成为了大家关心的主要话题&#xff0c;但网络是一把双刃剑&#xff0c;即便企业做好了安全防护&#xff0c;依旧会给企业的数据安全带…

Prometheus+Grafana 监控

第1章Prometheus 入门 Prometheus 受启发于 Google 的 Brogmon 监控系统&#xff08;相似的 Kubernetes 是从 Google的 Brog 系统演变而来&#xff09;&#xff0c;从 2012 年开始由前 Google 工程师在 Soundcloud 以开源软件的 形式进行研发&#xff0c;并且于 2015 年早期对…

如何在Linux搭建Inis网站,并发布至公网实现远程访问【内网穿透】

如何在Linux搭建Inis网站&#xff0c;并发布至公网实现远程访问【内网穿透】 前言1. Inis博客网站搭建1.1. Inis博客网站下载和安装1.2 Inis博客网站测试1.3 cpolar的安装和注册 2. 本地网页发布2.1 Cpolar临时数据隧道2.2 Cpolar稳定隧道&#xff08;云端设置&#xff09;2.3.…

论文阅读:How Do Neural Networks See Depth in Single Images?

是由Technische Universiteit Delft(代尔夫特理工大学)发表于ICCV,2019。这篇文章的研究内容很有趣,没有关注如何提升深度网络的性能&#xff0c;而是关注单目深度估计的工作机理。 What they find&#xff1f; 所有的网络都忽略了物体的实际大小&#xff0c;而关注他们的垂直…

全球最强开源大模型一夜易主!谷歌Gemma 7B碾压Llama 2 13B,今夜重燃开源之战

一声炸雷深夜炸响&#xff0c;谷歌居然也开源LLM了&#xff1f;&#xff01; 这次&#xff0c;重磅开源的Gemma有2B和7B两种规模&#xff0c;并且采用了与Gemini相同的研究和技术构建。 有了Gemini同源技术的加持&#xff0c;Gemma不仅在相同的规模下实现SOTA的性能。 而且更令…

嵌入式学习-qt-Day3

嵌入式学习-qt-Day3 一、思维导图 二、作业 完善对话框&#xff0c;点击登录对话框&#xff0c;如果账号和密码匹配&#xff0c;则弹出信息对话框&#xff0c;给出提示”登录成功“&#xff0c;提供一个Ok按钮&#xff0c;用户点击Ok后&#xff0c;关闭登录界面&#xff0c;跳…

Transformer 架构—Encoder-Decoder

文章目录 前言 一、Encoder 家族 1. BERT 2. DistilBERT 3. RoBERTa 4. XML 5. XML-RoBERTa 6. ALBERT 7. ELECTRA 8. DeBERTa 二、Decoder 家族 1. GPT 2. GPT-2 3. CTRL 4. GPT-3 5. GPT-Neo / GPT-J-6B 三、Encoder-Decoder 家族 1. T5 2. BART 3. M2M-100 4. BigBird 前言 …

SpringBoot---集成MybatisPlus

介绍 使用SpringBoot集成MybatisPlus框架。 第一步&#xff1a;添加MybatisPlus依赖 <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.4</version> </dependenc…

MIT6.S081学习——一、环境搭建、资料搜集

MIT6.S081学习——一、环境搭建、资料搜集 1、环境准备2、资料搜集2、环境搭建2.1 Linux环境准备2.2 环境搭建2.2.1 根据官网指导代码进行相关工具的安装2.2.2 下载并且编译QEMU 3、VSCode远程连接Ubuntu3.1 安装remote-ssh3.1.1 安装插件3.1.2 配置config文件 3.2 Ubuntu安装S…

springcloud:2.OpenFeign 详细讲解

OpenFeign 是一个基于 Netflix 的 Feign 库进行扩展的工具,它简化了开发人员在微服务架构中进行服务间通信的流程,使得编写和维护 RESTful API 客户端变得更加简单和高效。作为一种声明式的 HTTP 客户端,OpenFeign 提供了直观的注解驱动方式,使得开发人员可以轻松定义和调用…

Redis突现拒绝连接问题处理总结

一、问题回顾 项目突然报异常 [INFO] 2024-02-20 10:09:43.116 i.l.core.protocol.ConnectionWatchdog [171]: Reconnecting, last destination was 192.168.0.231:6379 [WARN] 2024-02-20 10:09:43.120 i.l.core.protocol.ConnectionWatchdog [151]: Cannot reconnect…

win32 汇编读文件

做了2个小程序&#xff0c;没有读成功&#xff1b;文件打开了&#xff1b; .386.model flat, stdcalloption casemap :noneinclude windows.inc include user32.inc includelib user32.lib include kernel32.inc includelib kernel32.lib include Comdlg32.inc includelib …

Pormise---如何解决javascript中回调的信任问题?【详解】

如果阅读有疑问的话&#xff0c;欢迎评论或私信&#xff01;&#xff01; 本人会很热心的阐述自己的想法&#xff01;谢谢&#xff01;&#xff01;&#xff01; 文章目录 回调中的信任问题回调给我们带来的烦恼&#xff1f;调用过早调用过晚调用的次数太少或太多调用回调时未能…

数据结构之链表经典算法QJ题目

目录 单链表经典算法题目1. 单链表相关经典算法OJ题&#xff1a;移除链表元素思路一&#xff1a;思路二&#xff1a; 2. 单链表相关经典算法QI题&#xff1a;链表的中间节点思路一思路二 3. 单链表相关经典算法QJ题&#xff1a;反转链表思路一思路二 4. 单链表相关经典算法QJ题…

pytest 框架自动化测试

随笔记录 目录 1. 安装 2. 安装pytest 相关插件 2.1 准备阶段 2.2 安装 2.3 验证安装成功 3. pytest测试用例的运行方式 3.1 主函数模式 3.1.1 主函数执行指定文件 3.1.2 主函数执行指定模块 3.1.3 主函数执行某个文件中的某个类、方法、函数 3.1.4 主函数执行生…

学习鸿蒙背后的价值?星河版开放如何学习?

现在是2024年&#xff0c;华为在1月18开展了鸿蒙千帆起仪式发布会。宣布了鸿蒙星河版&#xff0c;并对开发者开放申请&#xff0c;此次发布会主要是说明了&#xff0c;鸿蒙已经是全栈自研底座&#xff0c;鸿蒙星河版本的编程语言改为ArkTS/仓颉&#xff0c;内核改为鸿蒙原生内核…

操作系统虚拟内存(上)

一、虚拟内存简要 1.为什么需要虚拟内存? 如果第一个程序在 2000 的位置写入一个新的值&#xff0c;将会擦掉第二个程序存放在相同位置上的所有内容&#xff0c;所以同时运行两个程序是根本行不通的&#xff0c;这两个程序会立刻崩溃&#xff0c;所以想要在这种情况下&#…

SQL 练习题目(入门级)

今天发现了一个练习SQL的网站--牛客网。里面题目挺多的&#xff0c;按照入门、简单、中等、困难进行了分类&#xff0c;可以直接在线输入SQL语句验证是否正确&#xff0c;并且提供了测试表的创建语句&#xff0c;也可以方便自己拓展练习&#xff0c;感觉还是很不错的一个网站&a…

OpenAI 的 GPTs 提示词泄露攻击与防护实战:防御卷(一)

前面的OpenAI DevDay活动上&#xff0c;GPTs技术的亮相引起了广泛关注。随着GPTs的创建权限开放给Plus用户&#xff0c;社区里迅速涌现了各种有趣的GPT应用&#xff0c;这些都是利用了Prompt提示词的灵活性。这不仅展示了技术的创新潜力&#xff0c;也让人们开始思考如何获取他…