接受一个
getter
函数,并根据getter
的返回值返回一个不可变的响应式 ref 对象
computed
的特点是缓存,计算属性是一个effect
,计算属性收集外层effect
多次取值 如果依赖的属性未发生变化是不会重新计算的
__dirty
计算属性中的缓存标识,如果依赖有变化重新执行(this.effect的 调度函数执行)没变化不重新执行
- 1、在
constructor
中将用户的getter
放到effec
中, 能对getter函数 使用到的属性进行依赖收集
activeEffect会变为 getter - 2、创建ReactiveEffect时,传入
scheduler
函数,稍后依赖的属性变化时调用此方法 - 3、取值的时候进行依赖收集
- 4、
_dirty=true
是脏值, 执行函数,其实执行的是 scheduler 调度函数在其中 触发更新triggerEffects
使用
const {effect, reactive} = VueReactivity
const state = reactive({firstname: 'l',lastname: 'yp'})
const fullname = computed({
get(){
return state.firstname + state.lastname
},
set(newValue){
console.log(newValue)
}
})
const fullname2 = computed(() => {
return state.firstname + state.lastname
})
// console.log(fullname.value)
// fullname.value = 'jdlyp'
effect(()=>{
app.innerHtml = fullname.value
})
setTimeout(()=>{
state.firstname = 'jdl'
}, 1000)
入口函数
export const isFunction = (value) => {
return typeof value === 'function'
}
export const computed = (getterOrOptions) => {
let onlyGetter = isFunction(getterOrOptions)
let getter;
let setter;
if (onlyGetter) {
getter = getterOrOptions;
setter = () => { console.log('on set') }
} else {
getter = getterOrOptions.get;
setter = getterOrOptions.set;
}
// 创建计算属性
return new ComputedRefImpl(getter, setter)
}
实现
// 见第二节:vue3-Reactivity模块-effect
import {
activeEffect,
ReactiveEffect,
trackEffects,
triggerEffects
} from './effect'
class ComputedRefImpl {
public effect;
public _value;
public dep= new Set;
public _dirty = true; // 数据是否更新的标识也叫缓存标识也叫脏数据标识 默认应该取值的时候进行计算
public __v_isReadonly = true
public __v_isRef = true
constructor(public getter, public setter) {
// 将用户的getter放到effec中, 能对getter函数进行依赖收集 activeEffect会变为
// 用getter 生成的 effect
// 这里边 firstname 和lastname就会被getter收集起来
// 创建ReactiveEffect时,传入`scheduler`调度函数,稍后依赖的属性变化时调用此方法
this.effect = new ReactiveEffect(getter, () => {
// 稍后依赖属性变化会执行此调度函数
if(!this._dirty){ // 依赖的值变化更新dirty并触发更新
this._dirty = true;
// 实现一个触发更新
triggerEffects(this.dep)
}
})
}
// 类中的属性访问器 底层就是Objecy.defineProperty
get value() {
// 取值的时候进行依赖收集
if(activeEffect){
trackEffects(this.dep);
}
if(this._dirty){ // 第一次是true 说明是脏值, 执行函数
this._dirty = false;
// 其实执行的是 scheduler 调度函数在其中 触发更新triggerEffects
this._value = this.effect.run();
}
return this._value
}
set value(newValue) {
this.setter(newValue)
}
}
依赖收集和触发更新
是从上一章
vue3-Reactivity模块-effect
中 的trigger、 track
函数中提取的
// get的时候 依赖收集
export function trackEffects(dep) { // 收集dep 对应的effect
let shouldTrack = !dep.has(activeEffect)
if (shouldTrack) {
dep.add(activeEffect);
activeEffect.deps.push(dep);
}
}
// 更新effect
export function triggerEffects(effects) {
effects = new Set(effects);
for (const effect of effects) {
if (effect !== activeEffect) { // 如果effect不是当前正在运行的effect
if (effect.scheduler) {
effect.scheduler()
} else {
effect.run(); // 重新执行一遍
}
}
}
}