你可能會想說“為什么我們不直接使用計算屬性來處理這些副作用?”
這是我們的例子:
let product = reactive({ price: 5, quantity: 2 })
let salePrice = ref(0)
let total = 0
effect(() => {
salePrice.value = product.price * 0.9
})
effect(() => {
total = salePrice.value * product.quantity
})
我們來轉換一下:
let product = reactive({ price: 5, quantity: 2 })
let salePrice = computed(() => {
return product.price * 0.9
})
let total = computed(() => {
return salePrice.value * product.quantity
})
請注意salePrice計算屬性如何包含在total計算屬性中,我們需要使用.value訪問它。看起來我們正在創(chuàng)建另一個ref響應式引用。
下面是如何定義我們的computed函數(shù):
function computed(getter) {
let result = ref() // 創(chuàng)建一個新的響應式引用
effect(() => (result.value = getter())) // 將 result 的值設置為 getter 的返回值
return result // 返回這個 ref
}
這就是全部了。你可以在 Github 上找到完整代碼 computed.js.
規(guī)避了 Vue 2 中更改檢測警告的漏洞
值得一提的是,我們可以用響應式對象做一些 Vue 2 無法做到的事情。比如我們可以像這樣添加新的響應式式屬性:
let product = reactive({ price: 5, quantity: 2 })
...
product.name = 'Shoes'
effect(() => {
console.log(`Product name is now ${product.name}`)
})
product.name = 'Socks'
就如你期望的那樣,控制臺打印出:
Product name is now Shoes
Product name is now Socks
這在 Vue 2 中是不可能的,因為 Vue 2 的響應性是用 Object.defineProperty將getter和setter添加到單個對象屬性來實現(xiàn)的。現(xiàn)在有了 Proxy,我們可以毫無顧慮地添加新屬性,并且它們可以立即響應。
我們的代碼 VS Vue 3 源碼
您可能想知道,我們的代碼是否能大致與 Vue 3 源碼等效?
可以像下圖那樣操作:
- git clone vue-next
-
yarn install
-yarn build reactivity。 - 將在
packages/reactivity/dist/找到的reactivity.cjs.js引入到我們的示例文件頂部,代替我們自己寫的reactive、computed、effect方法。

var { reactive, computed, effect } = require('./reactivity.cjs')
// 后面的代碼都一樣,故省略
打印的結果依然是一樣:
Before updated quantity total (should be 9) = 9 salePrice (should be 4.5) = 4.5
After updated quantity total (should be 13.5) = 13.5 salePrice (should be 4.5) = 4.5
After updated price total (should be 27) = 27 salePrice (should be 9) = 9
Product name is now Shoes
Product name is now Socks
嗯,所以我們的響應式系統(tǒng)是不是和 Vue 的差不多了 ?但真實情況 Vue 的版本肯定要復雜得多。 讓我們來看看構成 Vue 3 的響應式系統(tǒng)的文件吧。
Vue 3 響應式源碼文件
我們在 Vue 3 源碼包/packages/reactivity/src/可以找到以下文件。它們是TypeScript (ts)文件,但你應該能夠讀懂它們(即使不熟悉TS)。
-
effect.ts - 定義了
effect函數(shù)(用來封裝可能包含響應式引用和對象的代碼)。還包含了 get 屬性時調用的track和 set 屬性調用的trigger。 -
baseHandlers.ts - 包含 Proxy
handlers,譬如get和set, 它們分別調用了track和trigger(來自effect.ts)。 -
reactive.ts - 定義了響應式語法的功能,它創(chuàng)建了一個 ES6 Proxy,并使用
get和set(來自basehandlers.ts)作為 proxy 的處理程序(handlers)。 -
ref.ts - 通過對象訪問器定義創(chuàng)建響應式引用的方法。還包含
toRefs,它將響應式對象轉換為訪問原始 proxy 的一個個響應式引用。 -
computed.ts - 定義了計算屬性,使用
effect和對象訪問器并且返回了一個類似Ref的對象。(和我們的實現(xiàn)稍微有點不同)。

這些文件包含了 Vue 響應式系統(tǒng)的核心功能。
Vue 3 響應式原理一 - Vue 3 Reactivity
Vue 3 響應式原理二 - Proxy and Reflect
Vue 3 響應式原理三 - activeEffect & ref
Vue 3 響應式原理四 - Computed Values & Vue 3 源碼