手写mini版本的Vue3--实现 ref 功能

728 阅读2分钟

介绍

开头继续感谢崔大的mini-vue 项目。

项目地址:github.com/cuixiaorui/…

强烈建议大家把项目 down 下来,运行一下,看VUE3 的整体运行逻辑。

创建一个基本的ref

创建一个ref 文件,然后导出 ref 函数,我们就在 ref 函数里面实现逻辑。

ref 函数只需要返回一个RefImpl 类,就行可以,然后我们来实现这个类。


export function ref(value) {
return new RefImpl(value); 
}          

 class RefImpl {
  private _value: any;
  constructor(value) {
    this._value = value;
  }

  get value() {
    return this._value;
  }

}

这个就是简单的一个 ref 实现,可以实现当传入一个值的时候,我们可以通过.value 的形式拿到这个值

让 ref 具备响应式功能

我们要实现的逻辑就是跟 reactive 类似,只不过set 的逻辑是通过 值.value 的形式设置而已。

还有就再次调用的时候,但是不改值。我们不触发更新。

如果要实现 set,我们首先要做的事情就是依赖收集。

收集依赖

我们以前在 reactive 里面实现过了依赖收集,只不过我们当时收集的是根据key 收集很多deps,我们这个 ref 只有一个key,也就是只有一个 dep。

这样子我们就可以在 track 函数里面把添加依赖的方法抽离成 triggerEffects,然后track里面调用triggerEffects,在我们 ref 也可以调用dep。

get value() {
trackRefValue(this);
return this._value;
}

export function trackEffects(dep) {
  // 看看 dep 之前有没有添加过,添加过的话 那么就不添加了
  if (dep.has(activeEffect)) return;

  dep.add(activeEffect);
  activeEffect.deps.push(dep);
}

触发依赖

跟收集依赖类似,我们也可以把


 set value(newValue) {
      triggerEffects(this.dep);
      return this._value
  }

export function triggerEffects(dep) {
  for (const effect of dep) {
    if (effect.scheduler) {
      effect.scheduler()
    } else {
      effect.run()
    }
  }
}

我们通过 hasChanged 这个函数判断一下,是否value 有改变,如果有改变我们再去执行后续的逻辑,如果没有改变,就不需要了

 if (hasChanged(newValue, this._rawValue)) {
      this._rawValue = newValue;
      triggerEffects(this.dep);
    
 }

ref 对象转换为 reactive

根据传入的值来判断是不是一个对象,如果是对象,就掉用 reactive 来做转换。

set value(newValue) {
    if (hasChanged(newValue, this._rawValue)) {
    this._rawValue = newValue;
    this._value = convert(newValue);
    triggerEffects(this.dep);
   }
}


function convert(value) {
  return isObject(value) ? reactive(value) : value;
}



结尾

项目已经放到我的 GitHub 上面了,欢迎大家去start。

本次 commit地址:github.com/moyuhaokan/…

我的项目地址:github.com/moyuhaokan/…

再次推荐崔大的项目:github.com/cuixiaorui/…