<acronym id="s8ci2"><small id="s8ci2"></small></acronym>
<rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
<acronym id="s8ci2"></acronym>
<acronym id="s8ci2"><center id="s8ci2"></center></acronym>
0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

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

3天內不再提示

一文看懂Vue3響應式系統原理

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

響應式的基本概念

響應式是指當數據發生變化時,系統會自動更新與數據相關的 DOM 結構。

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

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

響應式的關鍵在于vue的依賴收集機制。

簡化模型

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

已知watcher函數,調用了一些“外部函數”:


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

能否設計一個依賴收集系統,使這些“外部函數”運行時,watcher也會隨之運行?

關鍵:如何判斷函數間的調用關系?

看似有點難,實際一點也不簡單,我們需要知道函數間調用關系。我們先看個例子:


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


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


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

運行結果我們可以看出watcher內部一定調用了A、B函數:

為啥?js是單線程的。

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

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

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

我們由此可以確定

函數watcher執行期間,凡是運行過的函數,一定是watcher內部調用過的函數

根據這個原理,我們設計依賴收集系統如下:


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

測試一下效果

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

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

看起來達到了要求。

將上面代碼優化一下,最終如下


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觸發了')
        
        trigger(A);
    }
}
function B (isTracking = false) {
    
}

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

Vue依賴收集模型

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

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

1. Proxy對象get監聽,set觸發

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


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

終端運行結果:

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

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

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

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

一個無關的屬性key的賦值也會觸發監聽函數!這不是我們想要的。為了精確監聽,還需要細化依賴收集系統。

2. “key”級依賴

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


// 依賴收集函數,這里精確到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);}// 依賴觸發函數,這里精確到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) {
        // 觸發到key        trigger(target, key);        target[key] = value    }})

這里試一下效果

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

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

這樣就實現了精確到屬性的監聽系統??吹竭@里,似乎完成的很不錯了,但是看到下面的例子:

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

這里value屬性由false變為true后,屬性data的就已不再參與監聽函數內的邏輯了;監聽函數不應該再響應data屬性,但實際上并沒有。因為依賴關系已經固化,data屬性只要變化就一定會觸發監聽,不管是否真的需要:

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

3. 分支切換

為了優化這一點,應將依賴關系實時更新,將多余的監聽去除。為此,vue采取的策略是:

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

審核編輯:黃飛

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

    關注

    3

    文章

    4117

    瀏覽量

    61507
  • DOM
    DOM
    +關注

    關注

    0

    文章

    16

    瀏覽量

    9536
  • 監聽系統
    +關注

    關注

    0

    文章

    7

    瀏覽量

    6399

原文標題:Vue3響應式系統原理

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

收藏 人收藏

    評論

    相關推薦

    vue3性能優化,IconPark編譯優化

    前端vue
    小凡
    發布于 :2022年09月08日 12:51:18

    Vue3中transtion動畫造成的渲染問題

    前端vue
    小凡
    發布于 :2022年09月08日 12:52:35

    vue-router的概念和用法

    vue:前端路由和vue-router
    發表于 03-06 13:28

    解析Vue代碼層面的優化

    項目首屏優化、Webpack 編譯配置優化等問題,所以我們仍然需要去關注 Vue 項目性能方面的優化,使項目具有更高效的性能、更好的用戶體驗。本文是作者通過實際項目的優化實踐進行總結而來,希望讀者讀完本文,有定的啟發思考,從而對自己的項目進行優化起到幫助。本文內容分為以
    發表于 10-27 11:39

    基于TypeScript實現Vue3.0指令組件拖拽

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

    vue-cli-----vue實例中template:'<App/>是什么意思?

    哪位大神知道vue-cli-----vue實例中template:'是什么意思嗎?
    發表于 11-05 07:02

    請問如何搭建vue環境?

    安裝node環境搭建vue項目環境vue項目目錄講解開始我們的第vue項目
    發表于 11-13 06:23

    vue嵌入瀏覽器的相關資料推薦

    vue嵌入瀏覽器 Vue-ico (vue-ico)Dead easy, Google Material Icons for Vue. 太簡單了,V
    發表于 12-21 08:20

    關于React和Vue產生一定的認知

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

    Linux安裝Vue環境

    Linux安裝Vue環境
    的頭像 發表于 01-13 14:09 ?799次閱讀

    Vue入門Vue的生命周期

    .生命周期 4.1生命周期是什么 Vue的生命周期, 就是Vue實例從創建到銷毀的過程.
    的頭像 發表于 02-06 16:16 ?694次閱讀
    <b class='flag-5'>Vue</b>入門<b class='flag-5'>Vue</b>的生命周期

    Vue入門之Vue定義

    Vue (讀音 /vju?/,類似于 view) 是一套用于構建用戶界面的漸進式JavaScript框架。 Vue 的核心庫只關注視圖層,也就是只處理頁面。 Vue提供的一套JS框架,通常稱為
    的頭像 發表于 02-06 16:41 ?888次閱讀
    <b class='flag-5'>Vue</b>入門之<b class='flag-5'>Vue</b>定義

    在iOS中集成Vue是什么

    上一節Vue在非瀏覽器環境下的嘗試我們利用了weex在vue中的dom實現成功的在非瀏覽器環境中Vue的實例,接下來我們將Vue集成到iOS當中,利用JavaScriptCore來實現
    的頭像 發表于 03-03 09:56 ?431次閱讀
    在iOS中集成<b class='flag-5'>Vue</b>是什么

    簡單介紹一下Vue中的響應式原理

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

    使用Vue3時遇到的一些問題

    Vue3 目前已經趨于穩定,不少代碼庫都已經開始使用它,很多項目未來也必然要遷移至 Vue3。本文記錄我在使用 Vue3 時遇到的一些問題,希望能為其他開發者提供幫助。
    的頭像 發表于 09-13 10:16 ?624次閱讀
    使用<b class='flag-5'>Vue3</b>時遇到的一些問題
    亚洲欧美日韩精品久久_久久精品AⅤ无码中文_日本中文字幕有码在线播放_亚洲视频高清不卡在线观看
    <acronym id="s8ci2"><small id="s8ci2"></small></acronym>
    <rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
    <acronym id="s8ci2"></acronym>
    <acronym id="s8ci2"><center id="s8ci2"></center></acronym>