vue3如何做依賴收集?

整體結(jié)論

  1. reactive做數(shù)據(jù)代理和負(fù)責(zé)依賴收集的觸發(fā)

  2. effect 做數(shù)據(jù)副作用的觸發(fā)函數(shù)粉怕,函數(shù)內(nèi)部自運行effect()觸發(fā)reactive的get

  3. track get的觸發(fā)做effectFn的快照收集,結(jié)構(gòu)用weakMap保存(下面詳細(xì)介紹),依賴收集已經(jīng)做好

  4. trigger 當(dāng)原始值刪除或者改變觸發(fā)reactive中set和deleteProperty執(zhí)行trigger架专,他把weakMap中的effectFn找出來然后重新執(zhí)行一遍

tips:建議與下面簡版源碼一起食用

weakMap中的數(shù)據(jù)結(jié)構(gòu)

單獨擰出來說,搞懂這個基本搞懂50%~

const obj = reactive({ a: 'a', b: { c: 1 } })//測試數(shù)據(jù)玄帕,生成的weakMap如下
image.png

三層結(jié)構(gòu):weakMap ==>Map ==>Set

  • weakMap:存儲對象和key的關(guān)系

    • key:target對象部脚,對應(yīng)的是effect中引用的層級深度,也就是說{ a: 'a', b: { c: 1 } }一個 { c: 1 }兩個 為什么是這樣設(shè)計的呢裤纹?因為proxy的回調(diào)target就是原始對象委刘,也在做深度響應(yīng)的時候丧没,通過對象去找也只會觸發(fā)一次trigger()
    • vaue:Map
  • Map:存儲key和effectFn的關(guān)系

    • key:對應(yīng)的每個層級的key a/b c
    • value:Set
  • Set:存儲對應(yīng)的effect函數(shù)

    • value:effect傳進來的函數(shù)做存儲

下面直接貼代碼~

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="aDom"></div>
    <div id="bDom"></div>
    <script>
        //數(shù)據(jù)響應(yīng)式
        function reactive(obj) {
            if (!isObject(obj)) {
                return obj
            }
            return new Proxy(obj, {
                get(target, key) {
                    //Reflect容錯率不錯,錯誤會正確的處理
                    const res = Reflect.get(target, key)
                    console.log('get', res)
                    track(target, key)
                    return isObject(res) ? reactive(res) : res
                },
                set(target, key, value) {
                    const res = Reflect.set(target, key, value)
                    console.log('set', key)
                    trigger(target, key)
                    return res
                },
                deleteProperty(target, key) {
                    const res = Reflect.deleteProperty(target, key)
                    console.log('del', key)
                    trigger(target, key)
                    return res
                }
            })
        }
        function isObject(obj) {
            return typeof obj === 'object'
        }
        // 保存函數(shù)
        const effectStack = []
        //保存映射關(guān)系的數(shù)據(jù)結(jié)構(gòu)
        const targetMap = new WeakMap()
        //觸發(fā)依賴收集
        function effect(fn) {
            //對照源碼簡化的高階函數(shù)锡移,可以從簡呕童,但是為了原滋原味
            const e = createReactiveEffect(fn)
            //自運行
            e()
            return e
        }
        function createReactiveEffect(fn) {
            //錯誤處理
            //effectStack 進棧
            //執(zhí)行完依賴收集后出棧保證effectStack最后值的獲取
            const effectFn = function () {
                try {
                    effectStack.push(effectFn)
                    fn()
                } finally {
                    effectStack.pop()
                }
            }
            return effectFn
        }
        //跟蹤函數(shù):負(fù)責(zé)依賴收集
        function track(target, key) {
            //1. 獲取effectFn
            const effect = effectStack[effectStack.length - 1]
            if (effect) {
                //用過target獲取對應(yīng)的map  每一層級的數(shù)據(jù)對應(yīng)一個targetMap   target:Map
                let depMap = targetMap.get(target)
                //首次進入depMap為空,需要創(chuàng)建
                if (!depMap) {
                    depMap = new Map()
                    targetMap.set(target, depMap)
                }
                // 2. 通過key獲取依賴集合set
                let deps = depMap.get(key)
                if (!deps) {
                    deps = new Set()
                    depMap.set(key, deps)
                }
                // 3. 放入effect
                deps.add(effect)
                //層級關(guān)系是 obj:{key:effect}  三層套娃   WeakMap ==> Map ==>Set
            }
        }
        //觸發(fā)函數(shù):track()相反操作,拿出映射關(guān)系淆珊,執(zhí)行所有的cbs
        function trigger(target, key) {
            const depMap = targetMap.get(target)
            if (!depMap) return
            const deps = depMap.get(key)
            if (deps) {
                deps.forEach(dep => dep())
            }
        }
        const obj = reactive({ a: 'a', b: { c: 1 } })
        //源碼中  這里做虛擬dom的path夺饲,這里簡單的用textContent改變頁面上的值做響應(yīng)式
        effect(() => {
            aDom.textContent = obj.a
            console.log('effect1', obj.a)
        })
        //深層次的副作用
        effect(() => {
            bDom.textContent = obj.b.c
            console.log('effect2', obj.b.c)
        })
        setTimeout(() => {
            obj.a = '777'
            //當(dāng)改變深度子節(jié)點 只會觸發(fā)一次trigger 因為weakMap是以obj的target(遞歸中就是每一層的對象)存儲的
            obj.b.c = "666"
        }, 2000)
    </script>
</body>
</html>

都看到這里了~三連我就不要了,點個贊就行~哈哈

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末施符,一起剝皮案震驚了整個濱河市往声,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌戳吝,老刑警劉巖浩销,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異骨坑,居然都是意外死亡撼嗓,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進店門欢唾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人粉捻,你說我怎么就攤上這事礁遣。” “怎么了肩刃?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵祟霍,是天一觀的道長。 經(jīng)常有香客問我盈包,道長沸呐,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任呢燥,我火速辦了婚禮崭添,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘叛氨。我一直安慰自己呼渣,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布寞埠。 她就那樣靜靜地躺著屁置,像睡著了一般。 火紅的嫁衣襯著肌膚如雪仁连。 梳的紋絲不亂的頭發(fā)上蓝角,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天,我揣著相機與錄音,去河邊找鬼使鹅。 笑死揪阶,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的并徘。 我是一名探鬼主播遣钳,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼麦乞!你這毒婦竟也來了蕴茴?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤姐直,失蹤者是張志新(化名)和其女友劉穎倦淀,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體声畏,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡撞叽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了插龄。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片愿棋。...
    茶點故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖均牢,靈堂內(nèi)的尸體忽然破棺而出糠雨,到底是詐尸還是另有隱情,我是刑警寧澤徘跪,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布甘邀,位于F島的核電站,受9級特大地震影響垮庐,放射性物質(zhì)發(fā)生泄漏松邪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一哨查、第九天 我趴在偏房一處隱蔽的房頂上張望逗抑。 院中可真熱鬧,春花似錦解恰、人聲如沸锋八。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽挟纱。三九已至,卻和暖如春腐宋,著一層夾襖步出監(jiān)牢的瞬間紊服,已是汗流浹背檀轨。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留欺嗤,地道東北人参萄。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像煎饼,于是被迫代替她去往敵國和親讹挎。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,514評論 2 348

推薦閱讀更多精彩內(nèi)容