我正在参加「掘金·启航计划」
1.computed()定义
接受一个 getter 函数,返回一个只读的响应式 ref
对象。该 ref 通过 .value
暴露 getter
函数的返回值。它也可以接受一个带有 get
和 set
函数的对象来创建一个可写的 ref 对象
通过上面的定义,我们可以知道computed
可以分成两种情况:
1.创建一个只读的计算属性ref
:
let person = reactive({
firstName:'张',
lastName:'三'
})
let fullName = computed(()=>{
return person.firstName + '-' + person.lastName
})
2.创建一个可写的计算属性ref
:
let fullName = computed({
get(){
return person.firstName + '-' + person.lastName
},
set(value){
const nameArr = value.split('-')
person.firstName = nameArr[0]
person.lastName = nameArr[1]
}
})
2.computed()实现
我们首先知道,computed()方法是默认不执行的
,只有在调用的时候才执行,并且在其依赖的对应属性未发生变化时,其值是被缓存住的,多次调用,依旧使用的是缓存里面的
值。
const age = ref(18)
const myAge = computed(() => { // 此方法默认不会被执行
return age.value + 10;
})
//当访问属性的时候执行
console.log(myAge.value)
console.log(myAge.value)// 缓存
age.value = 100; // 更新age,myAge不会立刻重新计算
console.log( myAge.value) // 再次计算最新值
那我们理解了computed的一些特性后,就大致可以把computed组成结构分为:effect(lazy)
+ scheduler
+ 缓存
的标识,这三大部分,那我们就来实现computed方法
1.定义computed函数
- 简单表明一下,computed中可以传递的是一个对象,或者是一个函数,我们简单处理成我们想要的格式
export function computed(getOrOptions){
let getter
let setter
if(isFunction(getOrOptions)){
getter = getOrOptions
setter = () => {
console.warn('not set')
}
}else{
getter = getOrOptions.get
setter = getOrOptions.set
}
return new ComputedRefImpl(getter,setter)
}
2.ComputedRefImpl类的实现
- 定义一个
_dirty
变量, getter
的执行,会让我们拿到当前函数的返回值- 包裹成为effect函数,当值再次变化时,会执行effect函数
class ComputedRefImpl {
public _dirty = true
public _value
public effect
constructor(public getter, public setter) {
this.effect = effect(getter,{
lazy:true,
})
}
get value(){
if(this._dirty){
this._value = this.effect()
this._dirty =false
}
return this._value
}
set value(newVal){
this.setter(newVal)
}
}
3.如何让值发生变化时,我们让dirty变为true,再次重新计算
- 加上scheduler(调度机),我们只让dirty变为true
class ComputedRefImpl {
public _dirty = true
public _value
public effect
constructor(public getter, public setter) {
this.effect = effect(getter,{
lazy:true,
scheduler:() => {
if(!this._dirty){
this._dirty = true
}
}
})
}
}
- 思考,scheduler的执行时间,当然是在effects列表执行时一起执行
effects.forEach((effect: any) => {
if(effect.options.scheduler){
effect.options.scheduler(effect);
}else{
effect();
}
})
4.收集computed的依赖
class ComputedRefImpl {
public _dirty = true
public _value
public effect
constructor(public getter, public setter) {
this.effect = effect(getter,{
lazy:true,
scheduler:() => {
if(!this._dirty){
this._dirty = true
trigger(this,'set';'value')
}
}
})
}
get value(){
if(this._dirty){
this._value = this.effect()
this._dirty =false
}
track(this,'get','value')
return this._value
}
set value(newVal){
this.setter(newVal)
}
}
5.贴上完整代码
import { isFunction } from "@vue/shared/src";
import { effect, track, trigger } from "./effect";
class ComputedRefImpl{
public _dirty = true; // 默认取值时不要用缓存
public _value;
public effect;
constructor(getter,public setter){ // ts 中默认不会挂载到this上
this.effect = effect(getter,{
lazy:true, // 默认不执行
scheduler:()=>{
if(!this._dirty){
this._dirty = true;
trigger(this,TriggerOrTypes.SET,'value')
}
}
})
}
get value(){ // 计算属性也要收集依赖
if(this._dirty){
this._value = this.effect(); // 会将用户的反回值返回
this._dirty = false;
}
track(this,TrackOpTypes.GET,'value')
return this._value;
}
set value(newValue){
this.setter(newValue)
}
}
export function computed(getterOrOptions){
let getter;
let setter;
if(isFunction(getterOrOptions)){
getter = getterOrOptions;
setter = () =>{
console.warn('computed value must be readonly')
}
}else{
getter = getterOrOptions.get;
setter = getterOrOptions.set;
}
return new ComputedRefImpl(getter,setter)
}
© 版权声明
文章版权归作者所有,未经允许请勿转载,侵权请联系 admin@trc20.tw 删除。
THE END