你可能会想说“为什么我们不直接使用计算属性来处理这些副作用?”
这是我们的例子:
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
访问它。看起来我们正在创建另一个ref
响应式引用。
下面是如何定义我们的computed
函数:
function computed(getter) {
let result = ref() // 创建一个新的响应式引用
effect(() => (result.value = getter())) // 将 result 的值设置为 getter 的返回值
return result // 返回这个 ref
}
这就是全部了。你可以在 Github 上找到完整代码 computed.js.
规避了 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
添加到单个对象属性来实现的。现在有了 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
嗯,所以我们的响应式系统是不是和 Vue 的差不多了 ?但真实情况 Vue 的版本肯定要复杂得多。 让我们来看看构成 Vue 3 的响应式系统的文件吧。
Vue 3 响应式源码文件
我们在 Vue 3 源码包/packages/reactivity/src/
可以找到以下文件。它们是TypeScript (ts)文件,但你应该能够读懂它们(即使不熟悉TS)。
-
effect.ts - 定义了
effect
函数(用来封装可能包含响应式引用和对象的代码)。还包含了 get 属性时调用的track
和 set 属性调用的trigger
。 -
baseHandlers.ts - 包含 Proxy
handlers
,譬如get
和set
, 它们分别调用了track
和trigger
(来自effect.ts
)。 -
reactive.ts - 定义了响应式语法的功能,它创建了一个 ES6 Proxy,并使用
get
和set
(来自basehandlers.ts
)作为 proxy 的处理程序(handlers
)。 -
ref.ts - 通过对象访问器定义创建响应式引用的方法。还包含
toRefs
,它将响应式对象转换为访问原始 proxy 的一个个响应式引用。 -
computed.ts - 定义了计算属性,使用
effect
和对象访问器并且返回了一个类似Ref
的对象。(和我们的实现稍微有点不同)。
这些文件包含了 Vue 响应式系统的核心功能。
Vue 3 响应式原理一 - Vue 3 Reactivity
Vue 3 响应式原理二 - Proxy and Reflect
Vue 3 响应式原理三 - activeEffect & ref
Vue 3 响应式原理四 - Computed Values & Vue 3 源码
网友评论