const data = {
flag: true,
firstName: "z",
lastName: "ym"
}
const computed = {
fullName() {
if (!data.flag) return "没有名字"
return data.firstName + data.lastName
}
}
我们模拟 vue 的 data 和 computed 属性,现在我们需要实现下边功能
- 当 flag 为 true 的时候,设置 firstName 或 lastName 或 flag 的值,fullName 的值重新计算
- 当 flag 为 false 的时候,设置 firstName 或 lastName 的值,fullName 的值都不重新计算,只有设置 flag 的时候,fullName 的值才会重新计算
先思考一下,如果是自己写如何实现?
首先我们想要修改 data 某个属性的值的时候,更新 comouted 的属性,那我们就肯定需要订阅它们的 get 和 set 方法
function observer(obj, key) {
let value = obj[key]
Object.defineProperty(obj, key, {
get() {
return value
},
set(newVal) {
value = newVal
// 在这里设置值的时候,重新调用 computed.fullName 的方法,从而更新 fullName 的值
data.fullName = computed.fullName()
}
})
}
Object.keys(data).forEach(key => {
observer(data, key)
})
这时候我们会发现一个问题
我们并不知道哪个 data 属性的改变会对应更新哪个 computed 属性的值,所以我们需要收集对应的依赖
我们首先需要先思考一下,Dep 收集什么依赖?
Dep 希望修改了某个 data 属性的时候能做一些其他的事情,所以我们需要在 set 之前就把那些事情收集起来
首先我们创建一个 Dep 类,用来收集依赖和派发依赖
class Dep {
constructor() {
this.subs = []
}
notify() {
this.subs.forEach(sub => sub())
}
}
然后我们现在需要做两件事情
- 给所有的 data 原属性都加上 Dep 实例
- 在 get 的时候收集依赖,在 set 的时候派发依赖
既然要在 get 的时候收集依赖,那么我们必须要知道什么时候会触发属性的 get
我们假设有一个 vue 的 template 模板
<template>
<p>{
{ fullName }}</p>
</template>
- 页面在渲染的过程中,就必定会调用 data.fullName 这个属性的 get 方法
- 而 data.fullName = computed.fullName()
- 调用 computed.fullName 会触发 flag,firstName, lastName 的 get
- 那么自然在触发这些 get 的时候 他们就知道需要收集的是 data.fullName =
computed.fullName() 这个依赖了
那么,我们得出来的完整代码如下
const data = {
flag: true,
firstName: 'z',
lastName: 'ym'
}
const computed = {
fullName() {
if (!data.flag) return '没有任何东西'
return data.firstName + data.lastName
}
}
class Dep {
constructor() {
this.subs = []
}
notify() {
this.subs.forEach(sub => sub())
this.subs = []
}
}
let willCollectFunc = () => {
data.fullName = computed.fullName()
}
function observer(obj, key) {
let value = obj[key]
const dep = new Dep()
Object.defineProperty(obj, key, {
get() {
dep.subs.push(willCollectFunc)
return value
},
set(newVal) {
value = newVal
dep.notify()
}
})
}
Object.keys(data).forEach(key => {
observer(data, key)
})
data.fullName = computed.fullName()
console.log(data.fullName)
data.firstName = "h"
console.log(data.fullName)
data.flag = false
console.log(data.fullName)
data.firstName = "z"
console.log(data.fullName)
更详细的内容可以看以前我写的文章,之前写得不好,所以写更基础一些更容易理解
https://blog.csdn.net/weixin_42335036/article/details/109286637