先上仓库
用法
传入一个 getter 函数,返回一个默认不可手动修改的 ref 对象。
或者传入一个拥有 get 和 set 函数的对象,创建一个可手动修改的计算状态。
const obj = reactive({
foo: 'foo',
bar: 'bar'
})
const plusOne = computed({
get: () => {
return obj.foo + obj.bar
},
set: (val) => {
obj.foo = val - 1
}
})
effect(() => {
console.log('渲染结果',plusOne.value)
console.log(plusOne.value)
})
plusOne.value = 3
console.log(obj.foo)
实现分析
取值(plusOne.value)的时候需要执行get函数。那怎么在取值的时候去执行呢,用访问器属性
const test = {
get value() {
return 1
},
set value(newValue) {
console.log('设置值', newValue)
}
}
console.log('test', test.value)
test.value = 3
可见取值的时候会走get,设置值的时候会走get,我们就可以在get的时候去执行computed的get函数
export function computed (getterOrOptions: Function | WritableComputedOptions) {
let value
let getter:Function
let setter:Function
if (isFunction(getterOrOptions)) {
getter = getterOrOptions as Function
} else {
getter = (getterOrOptions as WritableComputedOptions).get
setter = (getterOrOptions as WritableComputedOptions).set
}
const obj = {
get value() {
value = getter()
return value
},
set value(newValue) {
setter(newValue)
}
}
return obj
}
这样好像没有问题,但是没有缓存,加上缓存试试,get的时候先看数据有没有变化,没有变化直接返回,有变化重新执行get,设置值的时候将缓存标志重置
const obj = {
get value() {
if (dirty) { 没有缓存,重新执行getter
value = getter()
dirty = false
}
track(obj, 'value')
return value
},
set value(newValue) {
dirty = true // 设置值的时候重置缓存标志
setter(newValue)
}
}
到这里数据倒是正常,但是如果有多个effect函数时,会出现有些effect不渲染,是因为我们在computed里面直接执行的getter,并未与effect做关联,所以在computed里我们不直接执行getter,而是让effect函数返回getter,执行effect的返回函数
const effectFn = effect(getter, {
lazy: true,
scheduler() {
if (!dirty) {
dirty = true // 通过调度器设置的时候将dirty置为true
trigger(obj, 'value')// 数据变化时,手动调用trigger触发响应
}
}
})
const obj = {
get value() {
if (dirty) {
value = effectFn() // 修改
dirty = false
}
track(obj, 'value') //手动调用track进行追踪
return value
},
set value(newValue) {
setter(newValue)
}
}
```