介绍
开头继续感谢崔大的mini-vue 项目。
强烈建议大家把项目 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/…