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.pswp.cn/news/697747.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/697747.shtml
英文地址,請注明出處:http://en.pswp.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 存在…

xxl_job系列---【GLUE(shell)模式如何傳多個參數,并被shell接收】

1.場景 我需要傳入多個參數&#xff0c;在執行任務時傳入的任務參數用逗號隔開&#xff0c;到shell中都被$1當做字符串接收到了&#xff0c;沒辦法&#xff0c;只能在shell中截取字符串&#xff0c;截成多個參數。 2.編輯入參 CRON:0 0 0 1 * ? #每月1號0點執行 點擊“任務管理…

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…

ubuntu20.04安裝webots仿真

ubuntu20.04安裝webots仿真 1.首先: wget -qO- https://cyberbotics.com/Cyberbotics.asc | sudo apt-key add - sudo apt-add-repository deb https://cyberbotics.com/debian/ binary-amd64/ sudo apt-get update sudo apt-get install webots .bashrc中添加環境變量:…

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…

C語言—自定義(構造)類型

2.20&#xff0c;17.56 1.只有當我們使用結構體類型定義變量/結構體數組,系統才會為結構體的成員分配內存空間,用于存儲對應類型的數據 2.strct 結構體 一起作為結構體類型標識符 嘿嘿暫時先這樣&#xff0c;我會回來改的1、定義一個表示公交線路的結構體&#xff0c;要…

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; 二叉樹的根是數組中的最大元素。左子樹是通過數組中最大值左邊部分構造出的最大二叉樹。右子樹是通過數組中最大值右邊部分構造出的最大二叉樹。 …

進程間傳遞 SQL 文的方法

SQL 文組成 SQL 文有 2 部分組成&#xff1a; SQL 原型&#xff0c;如&#xff1a;INSERT INTO test1 (id,name) VALUES (?,?)Args &#xff0c;? 號對應的值列表 有時&#xff0c;生成 SQL 文的進程和處理 SQL 文的進程&#xff0c;可能不是同一個 這里就涉及到如何高效…

免費搭建個人網盤

免費搭建一個屬于個人的網盤。 服務端 詳情請參考原網站的服務端下載和安裝虛擬磁盤Fuse4Ui可以支持把網盤內容掛載成系統的分區&#xff1b; 掛載工具效果圖&#xff1a;應用端應用端的下載 效果圖

藍橋杯第1374題——鍛造兵器

題目描述 小明一共有n塊鍛造石&#xff0c;第塊鍛造石的屬性值為ai. 現在小明決定從這n塊鍛造石中任取兩塊來鍛造兵器 通過周密計算&#xff0c;小明得出&#xff0c;只有當兩塊鍛造石的屬性值的差值等于C&#xff0c;兵器才能鍛造成功 請你幫小明算算&#xff0c;他有多少種選…

人工智能幾個關鍵節點:深藍,AlphaGo,ChatGPT,Sora

近30年&#xff0c;人工智能幾個關鍵節點&#xff1a;深藍&#xff0c;AlphaGo&#xff0c;ChatGPT&#xff0c;Sora 深藍&#xff1a; 1997年&#xff0c;深藍擊敗卡斯帕羅夫的比賽是通過一系列復雜的算法和策略實現的。深藍的開發團隊使用了一種名為“暴力搜索”的技術&…