成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

一張圖理清Vue 3.0的響應式系統

開發 前端
本文按照響應式系統的運行過程,劃分了”初始化“,”依賴收集“和”響應式“三個階段,分別闡述了各個階段所做的事情,應該能夠較好地幫助讀者理解其核心思路。

本文首發于我的博客:《一張圖理清 Vue 3.0 的響應式系統》 

隨著 Vue 3.0 Pre Alpha 版本的公布,我們得以一窺其源碼的實現。Vue 最巧妙的特性之一是其響應式系統,而我們也能夠在倉庫的 packages/reactivity 模塊下找到對應的實現。雖然源碼的代碼量不多,網上的分析文章也有一堆,但是要想清晰地理解響應式原理的具體實現過程,還是挺費腦筋的事情。經過一天的研究和整理,我把其響應式系統的原理總結成了一張圖,而本文也將圍繞這張圖去講述具體的實現過程。

文章涉及到的代碼我也已經上傳到倉庫,結合代碼閱讀本文會更為流暢哦!

一個基本的例子

Vue 3.0 的響應式系統是獨立的模塊,可以完全脫離 Vue 而使用,所以我們在 clone 了源碼下來以后,可以直接在 packages/reactivity 模塊下調試。

  1.  在項目根目錄運行 yarn dev reactivity,然后進入 packages/reactivity 目錄找到產出的 dist/reactivity.global.js 文件。
  2.  新建一個 index.html,寫入如下代碼:   
  1. <script src="./dist/reactivity.global.js"></script>  
  2.    <script>  
  3.    const { reactive, effect } = VueObserver  
  4.    const origin = {  
  5.      count: 0  
  6.    }  
  7.    const state = reactive(origin)  
  8.    const fn = () => {  
  9.      const count = state.count  
  10.      console.log(`set count to ${count}`)  
  11.    }  
  12.    effect(fn)  
  13.    </script> 

      3.  在瀏覽器打開該文件,于控制臺執行 state.count++,便可看到輸出 set count to 1。

在上述的例子中,我們使用 reactive() 函數把 origin 對象轉化成了 Proxy 對象 state;使用 effect() 函數把 fn() 作為響應式回調。當 state.count 發生變化時,便觸發了 fn()。接下來我們將以這個例子結合上文的流程圖,來講解這套響應式系統是怎么運行的。

初始化階段

在初始化階段,主要做了兩件事。

  1.  把 origin 對象轉化成響應式的 Proxy 對象 state。
  2.  把函數 fn() 作為一個響應式的 effect 函數。

首先我們來分析第一件事。

大家都知道,Vue 3.0 使用了 Proxy 來代替之前的 Object.defineProperty(),改寫了對象的 getter/setter,完成依賴收集和響應觸發。但是在這一階段中,我們暫時先不管它是如何改寫對象的 getter/setter 的,這個在后續的”依賴收集階段“會詳細說明。為了簡單起見,我們可以把這部分的內容濃縮成一個只有兩行代碼的 reactive() 函數: 

  1. export function reactive(target) {  
  2.   const observed = new Proxy(target, handler)  
  3.   return observed  

完整代碼在 reactive.js。這里的 handler 就是改造 getter/setter 的關鍵,我們放到后文講解。

接下來我們分析第二件事。

當一個普通的函數 fn() 被 effect() 包裹之后,就會變成一個響應式的 effect 函數,而 fn() 也會被立即執行一次。

由于在 fn() 里面有引用到 Proxy 對象的屬性,所以這一步會觸發對象的 getter,從而啟動依賴收集。

除此之外,這個 effect 函數也會被壓入一個名為”activeReactiveEffectStack“(此處為 effectStack)的棧中,供后續依賴收集的時候使用。

來看看代碼(完成代碼請看 effect.js): 

  1. export function effect (fn) {  
  2.   // 構造一個 effect  
  3.   const effect = function effect(...args) {  
  4.     return run(effect, fn, args)  
  5.   }  
  6.   // 立即執行一次  
  7.   effect()  
  8.   return effect  
  9.  
  10. export function run(effect, fn, args) {  
  11.   if (effectStack.indexOf(effect) === -1) {  
  12.     try {  
  13.       // 往池子里放入當前 effect  
  14.       effectStack.push(effect)  
  15.       // 立即執行一遍 fn()  
  16.       // fn() 執行過程會完成依賴收集,會用到 effect  
  17.       return fn(...args)  
  18.     } finally {  
  19.       // 完成依賴收集后從池子中扔掉這個 effect  
  20.       effectStack.pop()  
  21.     } 
  22.    }  

至此,初始化階段已經完成。接下來就是整個系統最關鍵的一步——依賴收集階段。

依賴收集階段

這個階段的觸發時機,就是在 effect 被立即執行,其內部的 fn() 觸發了 Proxy 對象的 getter 的時候。簡單來說,只要執行到類似 state.count 的語句,就會觸發 state 的 getter。

依賴收集階段最重要的目的,就是建立一份”依賴收集表“,也就是圖示的”targetMap"。targetMap 是一個 WeakMap,其 key 值是當前的 Proxy 對象 state代理前的對象origin,而 value 則是該對象所對應的 depsMap。

depsMap 是一個 Map,key 值為觸發 getter 時的屬性值(此處為 count),而 value 則是觸發過該屬性值所對應的各個 effect。

還是有點繞?那么我們再舉個例子。假設有個 Proxy 對象和 effect 如下: 

  1. const state = reactive({  
  2.   count: 0,  
  3.   age: 18  
  4. })  
  5. const effecteffect1 = effect(() => {  
  6.   console.log('effect1: ' + state.count)  
  7. })  
  8. const effecteffect2 = effect(() => {  
  9.   console.log('effect2: ' + state.age)  
  10. })  
  11. const effecteffect3 = effect(() => {  
  12.   console.log('effect3: ' + state.count, state.age)  
  13. }) 

那么這里的 targetMap 應該為這個樣子:

這樣,{ target -> key -> dep } 的對應關系就建立起來了,依賴收集也就完成了。代碼如下: 

  1. export function track (target, operationType, key) {  
  2.   const effect = effectStack[effectStack.length - 1]  
  3.   if (effect) {  
  4.     let depsMap = targetMap.get(target)  
  5.     if (depsMap === void 0) {  
  6.       targetMap.set(target, (depsMap = new Map()))  
  7.     }  
  8.     let dep = depsMap.get(key)  
  9.     if (dep === void 0) {  
  10.       depsMap.set(key, (dep = new Set()))  
  11.     }  
  12.     if (!dep.has(effect)) {  
  13.       dep.add(effect)  
  14.     }  
  15.   }  

弄明白依賴收集表 targetMap 是非常重要的,因為這是整個響應式系統核心中的核心。

響應階段

回顧上一章節的例子,我們得到了一個 { count: 0, age: 18 } 的 Proxy,并構造了三個 effect。在控制臺上看看效果:

效果符合預期,那么它是怎么實現的呢?首先來看看這個階段的原理圖:

當修改對象的某個屬性值的時候,會觸發對應的 setter。

setter 里面的 trigger() 函數會從依賴收集表里找到當前屬性對應的各個 dep,然后把它們推入到 effects 和 computedEffects(計算屬性) 隊列中,最后通過 scheduleRun() 挨個執行里面的 effect。

由于已經建立了依賴收集表,所以要找到屬性所對應的 dep 也就輕而易舉了,可以看看具體的代碼實現: 

  1. export function trigger (target, operationType, key) {  
  2.   // 取得對應的 depsMap  
  3.   const depsMap = targetMap.get(target)  
  4.   if (depsMap === void 0) {  
  5.     return  
  6.   }  
  7.   // 取得對應的各個 dep  
  8.   const effects = new Set()  
  9.   if (key !== void 0) {  
  10.     const dep = depsMap.get(key)  
  11.     dep && dep.forEach(effect => {  
  12.       effects.add(effect)  
  13.     })  
  14.   }  
  15.   // 簡化版 scheduleRun,挨個執行 effect  
  16.   effects.forEach(effect => {  
  17.     effect()  
  18.   })  

這里的代碼沒有處理諸如數組的 length 被修改的一些特殊情況,感興趣的讀者可以查看 vue-next 對應的源碼,或者這篇文章,看看這些情況都是怎么處理的。

至此,響應式階段完成。

總結

閱讀源碼的過程充滿了挑戰性,但同時也常常被 Vue 的一些實現思路給驚艷到,收獲良多。本文按照響應式系統的運行過程,劃分了”初始化“,”依賴收集“和”響應式“三個階段,分別闡述了各個階段所做的事情,應該能夠較好地幫助讀者理解其核心思路。最后附上文章實例代碼的倉庫地址,有興趣的讀者可以自行把玩:

tiny-reactive 

 

責任編輯:龐桂玉 來源: segmentfault
相關推薦

2021-02-07 09:01:10

Java并發編程

2019-09-11 10:12:12

華為

2015-03-10 10:15:27

AppleWatch開發Swift

2015-01-22 11:37:44

Android

2015-09-14 09:07:15

Java多線程

2012-07-20 17:24:51

HTML5

2019-03-18 15:00:48

SQLJoin用法數據庫

2018-02-13 14:56:24

戴爾

2020-09-12 16:45:49

Git

2025-03-11 10:58:00

2022-08-19 14:46:16

視覺框架

2015-06-24 10:51:10

iOS學習流程

2021-09-29 11:30:01

大數據技術架構

2023-12-05 19:31:39

分布式存儲

2015-09-23 10:04:03

開放數據

2015-10-29 15:09:32

信息圖數據

2023-09-05 08:53:51

2018-05-18 18:09:44

人工智能

2024-05-07 08:49:45

微服務架構模式
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 91精品国模一区二区三区 | 女同久久 | 狠狠狠| 成人久久久 | 亚洲欧美日韩高清 | 亚洲精品成人网 | 日韩国产一区 | 日韩在线精品 | 午夜在线影院 | 中文字幕一区二区三区日韩精品 | 国产精品久久二区 | 91网在线观看 | 午夜av电影 | 日韩中文av在线 | 一级毛片视频 | 亚洲激情av | 自拍视频网站 | 99久久国产综合精品麻豆 | 中文字幕国产视频 | 精品久久香蕉国产线看观看亚洲 | 日日艹夜夜艹 | a在线免费观看视频 | 91精品国产综合久久久久 | 91看片免费版 | 日韩亚洲视频 | 成人欧美一区二区三区白人 | 国产精品96久久久久久 | 国产乱码精品1区2区3区 | 青青青伊人 | 亚洲精品1 | 一区二区三区精品在线视频 | 久久久精品一区二区三区 | 成人影视网址 | 91新视频| 免费久久网| 久久国产精品视频 | 91av视频在线观看 | 亚洲天堂日韩精品 | 99re99| 中文字幕一页二页 | 日韩成人性视频 |