全市網(wǎng)站建設(shè)情況摸底調(diào)查百度網(wǎng)站關(guān)鍵詞排名查詢
Vue3的watch底層源碼主要是通過使用Proxy對象來實現(xiàn)的。在Vue3中,每個組件實例都會有一個watcher實例,用于監(jiān)聽組件數(shù)據(jù)的變化。當(dāng)組件數(shù)據(jù)發(fā)生變化時,watcher實例會觸發(fā)回調(diào)函數(shù),從而更新組件的視圖。
Vue3的watch底層源碼主要涉及到以下幾個部分:
- 創(chuàng)建watcher對象
- 收集依賴
- 觸發(fā)更新
下面對這三個部分進行詳細解讀。
- 創(chuàng)建watcher對象
在Vue3中,watcher對象是通過watchEffect
函數(shù)創(chuàng)建的。watchEffect
函數(shù)接收一個函數(shù)作為參數(shù),這個函數(shù)就是我們要監(jiān)聽的響應(yīng)式數(shù)據(jù)的getter函數(shù)。watchEffect
函數(shù)會在創(chuàng)建watcher對象時立即執(zhí)行這個函數(shù),并收集響應(yīng)式數(shù)據(jù)的依賴。
創(chuàng)建watcher對象的源碼如下:
function watchEffect(effect, options) {const watcher = new ReactiveEffect(effect, options)watcher.run()return () => {watcher.stop()}
}
其中,ReactiveEffect
是一個類,用來創(chuàng)建watcher對象。run
方法會執(zhí)行傳入的函數(shù),并收集依賴。stop
方法會停止watcher對象的執(zhí)行。
- 收集依賴
在Vue3中,依賴收集是通過track
函數(shù)實現(xiàn)的。track
函數(shù)接收兩個參數(shù):響應(yīng)式數(shù)據(jù)對象和屬性名。它會在getter函數(shù)執(zhí)行時,將當(dāng)前的watcher對象收集到響應(yīng)式數(shù)據(jù)對象的依賴列表中。
track
函數(shù)的源碼如下:
function track(target, key) {if (activeEffect === undefined) {return}let depsMap = targetMap.get(target)if (!depsMap) {targetMap.set(target, (depsMap = new Map()))}let dep = depsMap.get(key)if (!dep) {depsMap.set(key, (dep = new Set()))}if (!dep.has(activeEffect)) {dep.add(activeEffect)activeEffect.deps.push(dep)}
}
其中,targetMap
是一個全局的Map對象,用來存儲所有響應(yīng)式數(shù)據(jù)對象的依賴關(guān)系。activeEffect
是一個全局的watcher對象,表示當(dāng)前正在執(zhí)行的watcher對象。
- 觸發(fā)更新
在Vue3中,更新是通過trigger
函數(shù)實現(xiàn)的。trigger
函數(shù)接收兩個參數(shù):響應(yīng)式數(shù)據(jù)對象和屬性名。它會在setter函數(shù)執(zhí)行時,遍歷響應(yīng)式數(shù)據(jù)對象的依賴列表,依次執(zhí)行每個watcher對象的run
方法,從而觸發(fā)更新。
trigger
函數(shù)的源碼如下:
function trigger(target, key) {const depsMap = targetMap.get(target)if (!depsMap) {return}const dep = depsMap.get(key)if (!dep) {return}dep.forEach(effect => {if (effect.scheduler) {effect.scheduler()} else {effect.run()}})
}
其中,scheduler
是一個可選的參數(shù),表示watcher對象的調(diào)度函數(shù)。如果存在調(diào)度函數(shù),則會先執(zhí)行調(diào)度函數(shù),再執(zhí)行run
方法。
下面是watch底層源碼的主要實現(xiàn)步驟:
-
創(chuàng)建一個Proxy對象,用于監(jiān)聽數(shù)據(jù)的變化。
-
在Proxy對象的set方法中,觸發(fā)watcher實例的回調(diào)函數(shù)。
-
在watcher實例的回調(diào)函數(shù)中,執(zhí)行用戶定義的回調(diào)函數(shù),并將新舊值傳遞給回調(diào)函數(shù)。
-
在回調(diào)函數(shù)中,執(zhí)行用戶定義的邏輯,例如更新組件的視圖。
下面是watch底層源碼的簡單實現(xiàn):
function watch(source, callback) {const watcher = {callback: callback,oldValue: null,update() {const newValue = source();if (newValue !== this.oldValue) {this.callback(newValue, this.oldValue);this.oldValue = newValue;}}};watcher.update();return watcher;
}function reactive(obj) {return new Proxy(obj, {set(target, key, value) {target[key] = value;watchers.forEach(watcher => watcher.update());return true;}});
}const data = reactive({ count: 0 });const watchers = [];watchers.push(watch(() => data.count, (newValue, oldValue) => {console.log(`count changed from ${oldValue} to ${newValue}`);
}));data.count++; // count changed from 0 to 1
在上面的代碼中,我們定義了一個watch函數(shù)和一個reactive函數(shù)。watch函數(shù)用于創(chuàng)建watcher實例,reactive函數(shù)用于創(chuàng)建Proxy對象。當(dāng)數(shù)據(jù)發(fā)生變化時,Proxy對象的set方法會觸發(fā)watcher實例的回調(diào)函數(shù),從而更新組件的視圖。