前言:哈嘍,大家好,我是前端菜鳥的自我修養!今天給大家分享【深入剖析watch、computed、watchEffect的區別】,并提供具體代碼幫助大家深入理解,徹底掌握!原創不易,如果能幫助到帶大家,歡迎?收藏+關注?哦 💕
?
🌈🌈文章目錄
一、watch
1.使用語法
2.watch的實現原理?
二、computed
1.簡單使用
2.computed的實現原理
三、watchEffect
1.簡單使用
2.小結
3.watchEffect的實現原理
4.注意事項
1. 避免過度監聽
2. 異步操作需謹慎處理
3. 避免無限循環
四、實際開發當中該怎么去選擇
?1.watch
1.1 異步操作觸發
1.2 深度監聽
2.computed
2.1?派生數據?
2.2?性能優化
3.watchEffect
3.1?自動依賴追蹤
3.2?動態數據處理
五、總結
?
在Vue中,數據響應式是一個核心概念,它使得當數據變化時,相關的視圖會自動更新。為了更靈活地處理數據的變化,Vue提供了多種方式,其中包括watch、computed和watchEffect。
一句話概括:watch ?適用于需要有條件地監聽數據變化的場景,computed??適用于創建派生數據和性能優化,而watchEffect?適用于自動追蹤依賴的場景。在實際應用中,根據具體需求選擇合適的API可以更好地發揮Vue的響應式能力。
一、watch
1.使用語法
watch是Vue中一個非常強大的特性,它允許你監聽數據的變化并做出相應的反應。它有兩種用法:一是監聽一個具體的數據變化,二是監聽多個數據的變化。
// 監聽單個數據
watch('someData', (newVal, oldVal) => {// 做一些事情
});// 監聽多個數據
watch(['data1', 'data2'], ([newVal1, newVal2], [oldVal1, oldVal2]) => {// 做一些事情
});
2.watch的實現原理?
Vue中watch的實現主要依賴于Watcher這個核心類。當調用watch時,實際上是創建了一個Watcher實例,并將回調函數和需要監聽的數據傳遞給這個實例。
// 簡化版的watch實現
function watch(source, cb) {const watcher = new Watcher(source, cb);
}
Watcher類的構造函數接收兩個參數,分別是需要監聽的數據(可以是一個字符串,也可以是一個返回值的函數)和回調函數。在構造函數中,會對數據進行求值,然后將這個Watcher實例添加到數據的依賴列表中。
class Watcher {constructor(source, cb) {this.getter = typeof source === 'function' ? source : () => this.vm[source];this.cb = cb;this.value = this.get();}get() {pushTarget(this); // 將當前Watcher實例壓棧const value = this.getter.call(this.vm); // 觸發數據的getter,將當前Watcher實例添加到依賴列表中popTarget(); // 將當前Watcher實例出棧return value;}update() {const oldValue = this.value;this.value = this.get();this.cb(this.value, oldValue);}
}
?簡單來說,watch的實現原理就是通過Watcher實例來監聽數據的變化,當數據變化時,觸發update方法執行回調函數。
二、computed
1.簡單使用
computed用于計算派生數據。它依賴于其他響應式數據,并且只有在相關數據發生變化時才會重新計算。
computed(() => {return someData * 2;
});
2.computed的實現原理
computed的實現原理相對于watch更為復雜,它依賴于getter和setter的機制。在Vue中,computed的定義是一個包含get和set方法的對象。
const computed = {get() {return someData * 2;},set(value) {someData = value / 2;}
};
在computed的實現中,當訪問計算屬性時,實際上是執行了get方法,而在數據變化時,會執行set方法。這里主要使用了Object.defineProperty這個JavaScript的特性。
function createComputedGetter() {return function computedGetter() {const value = getter.call(this); // 執行計算屬性的get方法track(target, TrackOpTypes.GET, 'value'); // 添加依賴return value;};
}function createComputedSetter() {return function computedSetter(newValue) {setter.call(this, newValue); // 執行計算屬性的set方法trigger(target, TriggerOpTypes.SET, 'value'); // 觸發更新};
}function computed(getterOrOptions) {const getter = typeof getterOrOptions === 'function'? getterOrOptions: getterOrOptions.get;const setter = getterOrOptions.set;const cRef = new ComputedRefImpl(getter,setter,isFunction(getterOrOptions) || !getterOrOptions.get);return cRef;
}class ComputedRefImpl {// 構造函數constructor(getter, setter, isReadonly) {// ...this.effect = effect(getter, {lazy: true,scheduler: () => {if (!this._dirty) {this._dirty = true;triggerRef(this);}},});}// ...
}
在上述代碼中,createComputedGetter和createComputedSetter用于創建計算屬性的getter和setter。computed函數接收一個getter函數,并通過Object.defineProperty將getter和setter添加到計算屬性的引用對象中。
當計算屬性被訪問時,會觸發getter,此時會將當前計算屬性添加到依賴列表中。當計算屬性的依賴數據發生變化時,會觸發setter,并通過triggerRef觸發計算屬性的更新。
三、watchEffect
1.簡單使用
watchEffect()函數用于創建一個自動追蹤依賴的響應式副作用。它會在初始化時會立即執行一次,并自動追蹤在回調函數中使用的所有響應式數據,在這些數據發生變化時重新運行回調函數。
例如,在每當 todoId 的引用發生變化時使用偵聽器來加載一個遠程資源,如果用watch,是這么寫:
<template><div>Test</div>
</template><script setup>import { ref, watch } from 'vue'const todoId = ref(1)const data = ref(null)watch(todoId,async () => {const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${todoId.value}`)data.value = await response.json()console.log(data.value)},{ immediate: true })
</script>
打印:
?
但是用了watchEffect()
就可以簡化為:
<template><div>Test</div>
</template><script setup>import { ref, watchEffect } from 'vue'const todoId = ref(1)const data = ref(null)watchEffect(async () => {const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${todoId.value}`)data.value = await response.json()console.log(data.value)})
</script>
打印:
?
2.小結
兩者都立即打印了data。但下面的例子中,回調會立即執行,不需要指定 immediate: true。在執行期間,它會自動追蹤 todoId.value 作為依賴(和計算屬性類似)。每當 todoId.value 變化時,回調會再次執行。有了 watchEffect(),我們不再需要明確傳遞 todoId 作為源值。
從這個角度來看,watchEffect()的作用類似于Vue2中的computed,即依賴項發生變化,自己也跟著改變。但與computed不同,watchEffect()沒有返回值,而是直接執行回調函數。
3.watchEffect的實現原理
watchEffect是Vue3中引入的響應式API,它用于執行一個響應式函數,并在函數中響應式地追蹤其依賴。與watch不同,watchEffect不需要顯式地指定依賴,它會自動追蹤函數內部的響應式數據,并在這些數據變化時觸發函數重新執行。
首先,watchEffect的核心是依賴追蹤和觸發。Vue3中的響應式系統使用ReactiveEffect類來表示一個響應式的函數。
class ReactiveEffect {constructor(fn, scheduler = null) {// ...this.deps = [];this.scheduler = scheduler;}run() {// 執行響應式函數this.active && this.getter();}stop() {// 停止追蹤cleanupEffect(this);}
}export function watchEffect(effect, options = {}) {// 創建ReactiveEffect實例const runner = effect;const job = () => {if (!runner.active) {return;}if (cleanup) {cleanup();}// 執行響應式函數return runner.run();};// 執行響應式函數job();// 返回停止函數return () => {stop(runner);};
}
在上述代碼中,ReactiveEffect類表示一個響應式的函數。watchEffect函數接收一個響應式函數,并創建一個ReactiveEffect實例。在執行時,該實例會追蹤函數內部的響應式數據,并在這些數據變化時觸發函數重新執行。
watchEffect返回一個停止函數,用于停止對響應式數據的追蹤。
4.注意事項
1. 避免過度監聽
由于watchEffect()會追蹤使用到的所有響應式數據,因此要確保在回調函數中只使用必要的響應式數據,避免造成不必要的渲染開銷。
2. 異步操作需謹慎處理
由于watchEffect()會立即執行回調函數,如果在回調函數中進行異步操作,需要謹慎處理,以免導致意外行為或副作用。
<script setup>
import { watchEffect } from 'vue'// 它會自動停止
watchEffect(() => {})// ...這個則不會!
setTimeout(() => {watchEffect(() => {})
}, 100)
</script>
3. 避免無限循環
當在
watchEffect()
的回調中修改響應式數據時,可能會導致無限循環。要避免此問題,可以使用watch()
函數并設置?immediate: true
選項,或者使用ref
來存儲臨時數據。
四、實際開發當中該怎么去選擇
?1.watch
watch主要用于監聽特定的數據變化并執行回調函數。它可以監聽數據的變化,并在滿足一定條件時執行相應的操作。常見的使用場景包括:
1.1 異步操作觸發
當某個數據發生變化后,需要進行異步操作,比如發起一個網絡請求或執行一段耗時的操作。
watch(() => state.data, async (newData, oldData) => {// 異步操作await fetchData(newData);
});
1.2 深度監聽
監聽對象或數組的變化,并在深層次的數據變化時執行回調。
watch(() => state.user.address.city, (newCity, oldCity) => {console.log(`City changed from ${oldCity} to ${newCity}`);
});
2.computed
computed用于創建一個計算屬性,它依賴于其他響應式數據,并且只有在依賴數據發生變化時才重新計算。常見的使用場景包括:
2.1?派生數據?
根據現有的數據計算出一些派生的數據,而不必每次都重新計算。
const fullName = computed(() => `${state.firstName} ${state.lastName}`);
2.2?性能優化
避免不必要的重復計算,提高性能。
const result = computed(() => {// 避免重復計算if (someCondition) {return heavyCalculation();} else {return defaultResult;}
});
3.watchEffect
watchEffect用于執行一個響應式函數,并在函數內部自動追蹤依賴。它適用于不需要顯式指定依賴,而是在函數內部自動追蹤所有響應式數據變化的場景。常見的使用場景包括:
3.1?自動依賴追蹤
函數內部的所有響應式數據都被自動追蹤,無需顯式指定。
watchEffect(() => {console.log(`Count changed to ${state.count}`);
});
3.2?動態數據處理
處理動態變化的數據,無需手動管理依賴。
watchEffect(() => {// 處理動態變化的數據handleDynamicData();
})
五、總結
watch ?適用于需要有條件地監聽數據變化的場景,computed??適用于創建派生數據和性能優化,而watchEffect?適用于自動追蹤依賴的場景。在實際應用中,根據具體需求選擇合適的API可以更好地發揮Vue的響應式能力。
??好了,本文就到這里吧,點個關注再走嘛~?
🚀?個人簡介:7年開發經驗,某大型國企前端負責人,信息系統項目管理師、CSDN優質創作者、阿里云專家博主,分享前端相關技術與工作常見問題~
💟 作 ? ?者:前端菜鳥的自我修養??
📝 專 ? ?欄:vue從基礎到起飛
🌈 若有幫助,還請?關注?點贊?收藏??,不行的話我再努努力💪💪💪??
??更多專欄訂閱推薦:
👍?前端工程搭建
💕?前端常見問題匯總,避坑大全📝?javascript深入研究
???GIS地圖與大數據可視化