0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

一文看懂Vue3響應式系統(tǒng)原理

馬哥Linux運維 ? 來源:馬哥Linux運維 ? 2023-12-07 10:55 ? 次閱讀

響應式的基本概念

響應式是指當數(shù)據(jù)發(fā)生變化時,系統(tǒng)會自動更新與數(shù)據(jù)相關(guān)的 DOM 結(jié)構(gòu)。

在 Vue2 中,響應式系統(tǒng)的實現(xiàn)基于Object.defineProperty。然而,Object.defineProperty有一些局限,如:無法監(jiān)聽數(shù)組的變化、需要遍歷對象的每個屬性進行監(jiān)聽、性能開銷較大。

在 Vue3 中,響應式系統(tǒng)的實現(xiàn)基于 ES6 的Proxy對象。Proxy可以直接監(jiān)聽對象和數(shù)組的變化,而無需對每個屬性進行監(jiān)聽,從而大大提高性能。同時,Proxy也可以解決Object.defineProperty無法監(jiān)聽數(shù)組的問題。

響應式的關(guān)鍵在于vue的依賴收集機制。

簡化模型

為了更直觀的理解vue依賴收集的模型,我們先來看一個“簡單”的功能描述:

已知watcher函數(shù),調(diào)用了一些“外部函數(shù)”:


function watcher () {
    console.log('watcher start')
    函數(shù)1(); 
    函數(shù)2();
    console.log('watcher end')
}

能否設(shè)計一個依賴收集系統(tǒng),使這些“外部函數(shù)”運行時,watcher也會隨之運行?

關(guān)鍵:如何判斷函數(shù)間的調(diào)用關(guān)系?

看似有點難,實際一點也不簡單,我們需要知道函數(shù)間調(diào)用關(guān)系。我們先看個例子:


function A() { console.log('A') }
function B() { console.log('B') }
function C() { console.log('C') }
...


function watcher () {
    console.log('watcher start!')
    /* *這里調(diào)用了上面的某些函數(shù)* */
    console.log('watcher end!')
}
/* *這里運行了某些函數(shù)* */
watcher();


- watcher start!
- A
- B
- wathcer end! 
- C

運行結(jié)果我們可以看出watcher內(nèi)部一定調(diào)用了A、B函數(shù):

為啥?js是單線程的。

C函數(shù)一定在watcher外面嗎?不一定。例如:

function watcher () { console.log('start') A() B() setTimeout(()=>{ C() }) console.log('end') } watcher();

C函數(shù)這種咋辦?不管!我們只管肯定沒問題的!

我們由此可以確定

函數(shù)watcher執(zhí)行期間,凡是運行過的函數(shù),一定是watcher內(nèi)部調(diào)用過的函數(shù)

根據(jù)這個原理,我們設(shè)計依賴收集系統(tǒng)如下:


// 當前的監(jiān)聽函數(shù)
let activeEffect = null
// 副作用函數(shù)
function effect (watcher) {
    activeEffect = watcher
    // watcher執(zhí)行的期間就是依賴收集的階段
    watcher(true)
    activeEffect= null
}
// isTracking:是否是依賴收集階段
function A (isTracking = false) {
    if (isTracking) {
        // 依賴收集階段,effects就是A的監(jiān)聽函數(shù)集合
        A.effects = A.effects || new Set()
        A.effects.add(activeEffect)
    } else {
        // 依賴運行階段
        console.log('A觸發(fā)了')
        A.effects.forEach(fn => fn(true))
    }
}
function B (isTracking = false) {
    /*** 與A類似 ***/
} 

測試一下效果

2160c52e-94a1-11ee-939d-92fbcf53809c.png

218bd12e-94a1-11ee-939d-92fbcf53809c.png

看起來達到了要求。

將上面代碼優(yōu)化一下,最終如下


let activeEffect = null;
function effect (watcher) {    
    activeEffect = watcher;    
    watcher(true);
    
    activeEffect = null;
}


const bucket = new WeakMap();


function track (target) {
    const effects = bucket.get(target) || new Set();
    activeEffect && effects.add(activeEffect);
    bucket.set(target, effects);
}




function trigger (target) {
    bucket.get(target)?.forEach?.(fn => fn(true));
}
function A (isTracking = false) {
    if (isTracking) {
        
        track(A);
    } else {
        console.log('A觸發(fā)了')
        
        trigger(A);
    }
}
function B (isTracking = false) {
    
}

這里將之前 A.effects = A.effects || new Set();依賴收集流程提取成track函數(shù),監(jiān)聽函數(shù)的觸發(fā)流程抽離為trigger函數(shù);這樣,我們實現(xiàn)了一個簡單的依賴收集系統(tǒng)。

Vue依賴收集模型

我們知道Vue3是通過Proxy實現(xiàn)的依賴收集流程,Proxy示例:

21b2e732-94a1-11ee-939d-92fbcf53809c.png

1. Proxy對象get監(jiān)聽,set觸發(fā)

Vue3中,Proxy代理數(shù)據(jù)在被讀取時“依賴收集”,在被賦值時會“觸發(fā)依賴”;我們試一下上面完成的依賴收集系統(tǒng),看下效果:


const data = {
    value: 1,
}
const proxyData = new Proxy(data, {
    get(target, key) {
        
        track(target);
        return target[key];
    },
    set(target, key, value) {
        
        trigger(target);
        target[key] = value;
    }
})

測試一下

測試代碼如下:

21b99622-94a1-11ee-939d-92fbcf53809c.png

終端運行結(jié)果:

21bed538-94a1-11ee-939d-92fbcf53809c.png

看起來效果不錯!但是下面的例子里有問題:

21e2b908-94a1-11ee-939d-92fbcf53809c.png

21e6c7be-94a1-11ee-939d-92fbcf53809c.png

一個無關(guān)的屬性key的賦值也會觸發(fā)監(jiān)聽函數(shù)!這不是我們想要的。為了精確監(jiān)聽,還需要細化依賴收集系統(tǒng)。

2. “key”級依賴

我們可以將對象的屬性作為基本單位進行依賴收集。改造如下:


// 依賴收集函數(shù),這里精確到keyfunction track (target, key) {    const effects = bucket.get(target) || new Map();    const keyMap = effects.get(key) || new Set();    effects.set(key, keyMap);    bucket.set(target, effects);    activeEffect && keyMap.add(activeEffect);}// 依賴觸發(fā)函數(shù),這里精確到keyfunction trigger (target, key) {    const effects = bucket.get(target);    if (!effects) return;    const keyMap = effects.get(key);    if (!keyMap) return;    keyMap.forEach(effect => effect());}
const data = {    value: 1}const proxyData = new Proxy(data, {    get(target, key) {
        // 具體到key進行收集        track(target, key);        return target[key]    },    set(target, key, value) {
        // 觸發(fā)到key        trigger(target, key);        target[key] = value    }})

這里試一下效果

21f8e12e-94a1-11ee-939d-92fbcf53809c.png

22082b5c-94a1-11ee-939d-92fbcf53809c.png

這樣就實現(xiàn)了精確到屬性的監(jiān)聽系統(tǒng)。看到這里,似乎完成的很不錯了,但是看到下面的例子:

2211c0d6-94a1-11ee-939d-92fbcf53809c.png

這里value屬性由false變?yōu)閠rue后,屬性data的就已不再參與監(jiān)聽函數(shù)內(nèi)的邏輯了;監(jiān)聽函數(shù)不應該再響應data屬性,但實際上并沒有。因為依賴關(guān)系已經(jīng)固化,data屬性只要變化就一定會觸發(fā)監(jiān)聽,不管是否真的需要:

222b1842-94a1-11ee-939d-92fbcf53809c.png

3. 分支切換

為了優(yōu)化這一點,應將依賴關(guān)系實時更新,將多余的監(jiān)聽去除。為此,vue采取的策略是:

每次監(jiān)聽函數(shù)運行前,都要將自己的依賴關(guān)系清除;然后在運行期間重建依賴關(guān)系。(版權(quán)歸掘金硬毛巾原作者所有,侵刪)

審核編輯:黃飛

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4371

    瀏覽量

    64277
  • DOM
    DOM
    +關(guān)注

    關(guān)注

    0

    文章

    18

    瀏覽量

    9704
  • 監(jiān)聽系統(tǒng)

    關(guān)注

    0

    文章

    7

    瀏覽量

    6462

原文標題:Vue3響應式系統(tǒng)原理

文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 0人收藏

    評論

    相關(guān)推薦
    熱點推薦

    解析Vue代碼層面的優(yōu)化

    項目首屏優(yōu)化、Webpack 編譯配置優(yōu)化等問題,所以我們?nèi)匀恍枰リP(guān)注 Vue 項目性能方面的優(yōu)化,使項目具有更高效的性能、更好的用戶體驗。本文是作者通過實際項目的優(yōu)化實踐進行總結(jié)而來,希望讀者讀完本文,有定的啟發(fā)思考,從而對自己的項目進行優(yōu)化起到幫助。本文內(nèi)容分為以
    發(fā)表于 10-27 11:39

    基于TypeScript實現(xiàn)Vue3.0指令組件拖拽

    最近在用vue3重構(gòu)后臺的個功能。個彈窗組件,彈出個表單。然后點擊提交。早上運維突然跑過來問我,為啥彈窗擋住了下邊的表格的數(shù)據(jù),我添加的時候,都沒法對照表格來看了。你必須給我解決
    發(fā)表于 11-04 06:58

    Vue框架的教程資料免費下載

    Vue套用于構(gòu)建用戶界面的漸進JavaScript框架。與其它大型框架不同的是,Vue 被設(shè)計為可以自底向上逐層應用。Vue 的核心庫
    發(fā)表于 03-18 08:00 ?0次下載
    <b class='flag-5'>Vue</b>框架的教程資料免費下載

    關(guān)于vue如何去水印的解決方法的介紹

    很多人都懂些簡單的電腦系統(tǒng)問題的解決方案,但是vue怎么去水印的解決思路卻鮮為人知,小編前幾天就遇到了vue怎么去水印的問題,于是準備整理
    發(fā)表于 03-24 17:33 ?3521次閱讀

    關(guān)于React和Vue產(chǎn)生定的認知

    Vue2 相較 Vue3 版本而言牢牢占據(jù)著大部分 Vue 開發(fā)者的視野,但是因為 Vue 官方已經(jīng)把 Vue3 作為默認的版本,所以在此同
    的頭像 發(fā)表于 11-02 13:18 ?1008次閱讀

    Vue入門之Vue定義

    Vue (讀音 /vju?/,類似于 view) 是套用于構(gòu)建用戶界面的漸進JavaScript框架。 Vue 的核心庫只關(guān)注視圖層,也就是只處理頁面。
    的頭像 發(fā)表于 02-06 16:41 ?1301次閱讀
    <b class='flag-5'>Vue</b>入門之<b class='flag-5'>Vue</b>定義

    如何使用springboot+vue搭建個人網(wǎng)站3

    Vue.js(讀音 /vju?/, 類似于 view)是個構(gòu)建數(shù)據(jù)驅(qū)動的 web 界面的漸進框架。Vue現(xiàn)在這么火,大家都懂。接下來讓我們來認識
    的頭像 發(fā)表于 02-14 16:05 ?1570次閱讀
    如何使用springboot+<b class='flag-5'>vue</b>搭建個人網(wǎng)站<b class='flag-5'>3</b>

    搭建基于Vue3+Vite2+Arco+Typescript+Pinia后臺管理系統(tǒng)模板

    今天我們就來快速搭建個基于Vue3+Vite2+Arco+Typescript+Pinia后臺管理系統(tǒng)模板。這樣可以幫大家快速制作自己的后臺模板
    的頭像 發(fā)表于 03-01 10:09 ?1013次閱讀
    搭建基于<b class='flag-5'>Vue3</b>+Vite2+Arco+Typescript+Pinia后臺管理<b class='flag-5'>系統(tǒng)</b>模板

    簡單介紹Vue中的響應原理

    自從 Vue 發(fā)布以來,就受到了廣大開發(fā)人員的青睞,提到 Vue,我們首先想到的就是 Vue響應
    的頭像 發(fā)表于 03-13 10:11 ?956次閱讀

    使用Vue3時遇到的些問題

    Vue3 目前已經(jīng)趨于穩(wěn)定,不少代碼庫都已經(jīng)開始使用它,很多項目未來也必然要遷移至 Vue3。本文記錄我在使用 Vue3 時遇到的些問題,希望能為其他開發(fā)者提供幫助。
    的頭像 發(fā)表于 09-13 10:16 ?1295次閱讀
    使用<b class='flag-5'>Vue3</b>時遇到的<b class='flag-5'>一</b>些問題

    看懂FPGA芯片投資框架.zip

    看懂FPGA芯片投資框架
    發(fā)表于 01-13 09:06 ?4次下載

    看懂PCB天線、FPC天線的特性.zip

    看懂PCB天線、FPC天線的特性
    發(fā)表于 03-01 15:37 ?33次下載

    看懂BLE Mesh

    看懂BLE Mesh
    的頭像 發(fā)表于 12-06 16:24 ?1865次閱讀
    <b class='flag-5'>一</b><b class='flag-5'>文</b><b class='flag-5'>看懂</b>BLE Mesh

    Vue3設(shè)計思想及響應源碼剖析

    作者:京東物流 喬盼盼 Vue3結(jié)構(gòu)分析 1、Vue2與Vue3的對比 ?對TypeScript支持不友好(所有屬性都放在了this對象上,難以推倒組件的數(shù)據(jù)類型) ?大量的API
    的頭像 發(fā)表于 12-20 10:24 ?399次閱讀

    看懂電感、磁珠和零歐電阻的區(qū)別

    電子發(fā)燒友網(wǎng)站提供《看懂電感、磁珠和零歐電阻的區(qū)別.docx》資料免費下載
    發(fā)表于 01-02 14:48 ?2次下載

    電子發(fā)燒友

    中國電子工程師最喜歡的網(wǎng)站

    • 2931785位工程師會員交流學習
    • 獲取您個性化的科技前沿技術(shù)信息
    • 參加活動獲取豐厚的禮品