vue3 響應(yīng)式數(shù)據(jù)庫——reactive
請仔細(xì)閱讀下面代碼,思考vue3是如何做響應(yīng)式數(shù)據(jù)的?
let temp // reactive 是使對象變成一個代理 const counter = reactive({ num: 0 }); // effect主要職責(zé)是開啟依賴收集,等待get的調(diào)用完成正常的依賴存儲 effect(() => (temp = counter.num)); // 觸發(fā)更新 counter.num = 1; 復(fù)制代碼在這里是不是有的人要說,咋們在日常開發(fā)中,直接在 vue 模板上里面寫一個ref 自動幫我們進(jìn)行了開啟依賴收集,當(dāng)我們調(diào)用get的時候去存儲依賴。事出反常必有妖
在vue源碼中的renderer.ts 中有這么這么個代碼片段
const mountComponent: MountComponentFn = ( initialVNode, container, anchor, parentComponent, parentSuspense, isSVG, optimized ) => { // ...省略其他 // 這里會調(diào)用一個方法setupRenderEffect setupRenderEffect( instance, initialVNode, container, anchor, parentSuspense, isSVG, optimized ) } const setupRenderEffect: SetupRenderEffectFn = ( instance, initialVNode, container, anchor, parentSuspense, isSVG, optimized ) => { const componentUpdateFn = () => { // 省略組件更新邏輯 } // 這一段話的意思是創(chuàng)建一個 ReactiveEffect 來保存每一個獨立的proxy, // 和effect的效果是一樣的 const effect = new ReactiveEffect( componentUpdateFn, () => queueJob(instance.update), instance.scope // track it in components effect scope ) } 復(fù)制代碼總結(jié)下,咋們在模板中使用 ref,reactive等是vue本身在渲染的時候就會把整個組件放入ReactiveEffect中進(jìn)行依賴收集,對外拋出一個run方法,run方法用于決定是否需要進(jìn)行依賴收集哦,對于ref處理普通數(shù)據(jù)準(zhǔn)備另開篇幅
reactive
reactive 是用于把對象變成一個代理對象,proxy

effect
effect 函數(shù)的作用是用于開啟收集依賴,返回run函數(shù)

baseHandlers
baseHandlers 是用于處理proxy里面的get和set的,當(dāng)proxy調(diào)用get和set的時候就會去觸發(fā)對應(yīng)的函數(shù)
get操作的流程如下:

set 操作的流程如下:

track
在get的時候,通知進(jìn)行依賴收集,
在進(jìn)行依賴收集的時候,緩存?zhèn)魅氲膶ο螅?/p>

這里在緩存依賴的步驟是:
第一步: 當(dāng)運行counter.num 的時候會觸發(fā)proxy的get方法第二步: 全局有一個targetMap是一個weakMap 來記錄couter這個對象是否存在,并且weakMap的key是 couter這個對象,存在則使用,不存在則創(chuàng)建,counter的value又是一個Map,里面記錄著counter里面的key和counter對象的key是一一對應(yīng)的第三步: 在map中判斷key是 num的是否存在,存在則使用,不存在則創(chuàng)建一個Set來保存當(dāng)前的ReactiveEffect 對象,ReactiveEffect 對象含有run方法等待trigger觸發(fā)export function track(target, type, key) { if (!isTracking()) { return; } // 1. 先基于 target 找到對應(yīng)的 dep // 如果是第一次的話,那么就需要初始化 let depsMap = targetMap.get(target); if (!depsMap) { // 初始化 depsMap 的邏輯 depsMap = new Map(); targetMap.set(target, depsMap); } let dep = depsMap.get(key); if (!dep) { dep = createDep(); depsMap.set(key, dep); } if (!dep.has(activeEffect)) { dep.add(activeEffect); (activeEffect as any).deps.push(dep); } } 復(fù)制代碼trigger
當(dāng)對于track來說,trigger需要做的事情就會簡單許多
export function trigger(target, type, key) { const depsMap = targetMap.get(target); if (!depsMap) return; // 暫時只實現(xiàn)了 GET 類型 // get 類型只需要取出來就可以 const dep = depsMap.get(key); // 省略其他邏輯 // 觸發(fā)run函數(shù) for (const effect of dep) { if (effect.scheduler) { // scheduler 可以讓用戶自己選擇調(diào)用的時機 // 這樣就可以靈活的控制調(diào)用了 // 在 runtime-core 中,就是使用了 scheduler 實現(xiàn)了在 next ticker 中調(diào)用的邏輯 effect.scheduler(); } else { // 觸發(fā)函數(shù) effect.run(); } } } 復(fù)制代碼自己實現(xiàn)
說了那么多,不如自己來實現(xiàn)一遍簡單的響應(yīng)式系統(tǒng)

源碼地址
掃描二維碼推送至手機訪問。
版權(quán)聲明:本文由財神資訊-領(lǐng)先的體育資訊互動媒體轉(zhuǎn)載發(fā)布,如需刪除請聯(lián)系。