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.hqwc.cn/news/485406.html

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

相关文章

[linux]进程间通信(IPC)———共享内存(shm)(什么是共享内存,共享内存的原理图,共享内存的接口,使用演示)

一、什么是共享内存 共享内存区是最快的&#xff08;进程间通信&#xff09;IPC形式。一旦这样的内存映射到共享它的进程的地址空间&#xff0c;这些进程间数据传递不再涉及到内核&#xff0c;换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。注意&#xff1a;…

Seata 的 AT 模式

目录 概述 Springcloud 整合 Seata 数据库脚本 服务依赖 Springboot 配置 代码改造 AT模式下的数据隔离 写隔离 读隔离 概述 Seata 的 AT 模式是 Seata 的默认模式&#xff0c;它的原理是依赖于数据库事务&#xff0c;以数据库事务保证本地事务分支特性&#xff0c;结合…

数据库管理-第153期 Oracle Vector DB AI-05(20240221)

数据库管理153期 2024-02-21 数据库管理-第153期 Oracle Vector DB & AI-05&#xff08;20240221&#xff09;1 Oracle Vector的其他特性示例1&#xff1a;示例2 2 简单使用Oracle Vector环境创建包含Vector数据类型的表插入向量数据 总结 数据库管理-第153期 Oracle Vecto…

navicat导出数据库表结构信息

需求阐述 要求导出某一数据库表中的所有表的结构&#xff0c;汇总成一个word 准备工作 拿到所有表名&#xff0c;在navicat中执行sql语句&#xff1a;show tables;然后点击导出结果&#xff0c;选择excel格式进行导出。 拿到该数据库所有表名后&#xff0c;在navicat中执行如…

20.scala视图界定

目录 概述实践代码执行 结束 概述 scala 中的视图界定 实践 代码 /*** 视图界定*/ object Genericity03 {def main(args: Array[String]): Unit {println(new MaxInt(1,2).compare)println(new MaxLong(1L,2L).compare)// 不行 // println(new MaxValue(1,2).compare)// …

十九、图像的放缩和插值

项目功能实现&#xff1a;对一张图像进行放大和缩小操作 按照之前的博文结构来&#xff0c;这里就不在赘述了 一、头文件 resizing.h #pragma once#include<opencv2/opencv.hpp>using namespace cv;class RESIZING { public:void resizing(Mat& image); };#pragma…

HCIP-OSPF综合实验、MGRE搭建、OSPF基本配置、重发布、路由聚合、NAT、加快收敛、更新认证

OSPF&#xff08;Open Shortest Path First&#xff09;是IETF组织开发的一个基于链路状态的内部网关协议&#xff08;Interior Gateway Protocol&#xff09;。 采用最短路径SPF&#xff08;Shortest Path First&#xff09;算法。通过链路状态通告LSA&#xff08;Link State …

APP的UI自动化demo(appium+java)

文章目录 appium连接手机java代码实现-第一版第二版-接入testng和隐式等待显示等待 appium连接手机 准备工作 1、查看连接手机模拟器是否连接成功&#xff0c;获取设备名称 执行命令&#xff1a;adb devices 2、查看android内核版本号—>paltformVersion 执行命令&#xf…

文件包含+文件上传漏洞(图片马绕过)

目录 一.文件包含二.文件上传三.图片马四.题目 一.文件包含 将已有的代码以文件形式包含到某个指定的代码中&#xff0c;从而使用其中的代码或者数据&#xff0c;一般是为了方便直接调用所需文件&#xff0c;文件包含的存在使得开发变得更加灵活和方便&#xff08;若对用户输入…

使用 JMeter 生成测试数据对 MySQL 进行压力测试

博主历时三年精心创作的《大数据平台架构与原型实现&#xff1a;数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行&#xff0c;点击《重磅推荐&#xff1a;建大数据平台太难了&#xff01;给我发个工程原型吧&#xff01;》了解图书详情&#xff0c;…

原型模式(Prototype Pattern) C++

上一节&#xff1a;建造者模式&#xff08;Builder Pattern&#xff09;C 文章目录 0.理论1.原型模式的核心组成&#xff1a;2.实现方法3.什么时候使用 1.实践步骤 1: 定义怪物原型步骤 2: 实现具体怪物原型步骤 3: 使用原型创建怪物 0.理论 原型模式&#xff08;Prototype P…

springmvc+ssm+springboot房屋中介服务平台的设计与实现 i174z

本论文拟采用计算机技术设计并开发的房屋中介服务平台&#xff0c;主要是为用户提供服务。使得用户可以在系统上查看房屋出租、房屋出售、房屋求购、房屋求租&#xff0c;管理员对信息进行统一管理&#xff0c;与此同时可以筛选出符合的信息&#xff0c;给笔者提供更符合实际的…