最近在学习vue3源码中的一些知识点,从源码层面来讲,发现vue3对比vue2来说,vue3更加精巧,代码量更少,这也就意味着有些地方需要花费更多的精力去理解,在网上找了源码视频学习了一下,自己花了150多行代码实现一下reactive、effect、computed,这只是最简单的实现,可以将代码直接复制运行;不需要任何配置~~
function reactive(target) { // reactive本质-返回一个proxy实例
return new Proxy(target, {
get(target, key, receiver) {
track(target, key)
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
const res = Reflect.set(target, key, value, receiver)
trigger(target, key)
return res
}
})
}
let targetMap = new WeakMap() // 依赖集合,实际上是一个类似json的map结构
/**最外层是一个WeakMap结构;target就是各个被proxy的对象,作为键;key就是各个属性,每个属性对应一个set,存储该属性关联的依赖
* {
* target1 : {
* key: [
* effect1,
* effect2
* ]
* },
* target2: {
* key1 : [],
* key2: []
* }
*
* }
* */
let activeEffect; // 当前effect(观察者)
const state = reactive({
name: 'xxxx',
age: 18
})
function effect(fn) { // effect函数实际上创建一个ReactiveEffect类的实例 ,并执行该实例的run方法
const _effect = new ReactiveEffect(fn)
_effect.run()
}
class ReactiveEffect { // 实际上就是一个观察者
active = true
deps = []
constructor(fn, scheduler) {
this.fn = fn
this.scheduler = scheduler
}
run() {
try {
activeEffect = this
return this.fn()
} finally {
activeEffect = null
}
}
}
function track(target, key) { // 依赖收集
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, depsMap = new Map())
}
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, dep = new Set())
}
trackEffects(dep)
}
function trackEffects(dep) {
const shouldTrack = !dep.has(activeEffect)
if (shouldTrack) {
dep.add(activeEffect)
activeEffect.deps.push(dep)
}
}
function trigger(target, key) { // 更新依赖-类似vue2种的notify
const depsMap = targetMap.get(target)
if (!depsMap) return
let deps = []
if (key !== undefined) {
deps.push(depsMap.get(key))
}
const effects = []
deps.forEach(dep => {
effects.push(...dep)
})
triggerEffects(effects)
}
function triggerEffects(effects) {
for (let effect of effects) {
if (effect !== activeEffect) {
if (effect.scheduler) {
return effect.scheduler()
}
effect.run()
}
}
}
effect(() => {
console.log(state.age) // state.age触发proxy中的get方法
})
setTimeout(() => {
state.age = 28 // 触发proxy中的set方法,此时trigger(更新)
}, 3000);
function computed(getterOrOptions) { // computed本质就是返回一个ComputedRefImpl实例
return new ComputedRefImpl(getterOrOptions)
}
class ComputedRefImpl { // computed类
dep;
_value;
_dirty = true;
effect; // computed本质就是存储一个观察者(effect)
constructor(getter, setter = () => { }) {
this.setter = setter
this.effect = new ReactiveEffect(getter, () => {
if (!this._dirty) {
this._dirty = true
triggerEffects(this.dep)
}
})
}
/**
* 通过类的 get 方法获取属性
*/
get value() { // 通过获取computed.value属性 触发effect.run()
trackEffects(this.dep || (this.dep = new Set()))
if (this._dirty) {
this._value = this.effect.run()
this._dirty = false
}
return this._value // computed.value本质就是 _value
}
set value(val) {
this.setter(val)
}
}
const myAge = computed(() => {
return state.age + 15 // 获取state.age时会触发proxy的get方法,此时age对应的dep会收集computed中创建的effect(观察者)
})
effect(() => {
console.log(myAge.value) // myAge.value本质调用 computedRefImpl中的get value()方法,并获取返回值
})
console.log(targetMap)