这几天复习一下vue的一些问题,又又又回到了那个另人头大的环节:原理。vue3的原理真的是看一次忘一次。这篇讲vue3的文章还是挺好的,连接在此:林三心画了8张图,最通俗易懂的Vue3响应式核心原理解析
其实vue3的响应式抽象出来的结构,就如下所示
当fn第一次访问obj(此时obj已经被proxy代理了)时,就会被放入指定的set中,
当obj中的值变了,proxy就会通知相应set中的函数再次运行。
再详情描述下数据结构:
- targetMap是WeakMap, 只能以对象为key,而值无所值~
- depsMap是Map,没什么特别的
- set用于放置函数,函数很难传递名字,所以就用set而不用map了。
里面我觉得最核心的是代码,每次忘记了看一看代码就可以想起来。 注: 个人经验来说,deps其实把他当做中文里的依赖,有点难以理解,因为会下意识的想分清依赖和被依赖,结合上面的图来看,个人感觉翻译成“被依赖”更好理解。
const targetMap = new WeakMap()
function track(target, key) {
// 如果此时activeEffect为null则不执行下面
// 这里判断是为了避免例如console.log(person.name)而触发track
// 注:因为console.log(person.name)这种函数不需要响应式!
if (!activeEffect) return
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())
}
dep.add(activeEffect) // 把此时的activeEffect添加进去
}
function trigger(target, key) {
let depsMap = targetMap.get(target)
if (depsMap) {
const dep = depsMap.get(key)
if (dep) {
dep.forEach(effect => effect())
}
}
}
function reactive(target) {
const handler = {
get(target, key, receiver) {
track(receiver, key) // 访问时收集依赖
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
Reflect.set(target, key, value, receiver)
trigger(receiver, key) // 设值时自动通知更新
}
}
return new Proxy(target, handler)
}
let activeEffect = null
function effect(fn) {
activeEffect = fn
activeEffect()
activeEffect = null
}
function ref(initValue) {
return reactive({
value: initValue
})
}
function computed(fn) {
const result = ref()
effect(() => result.value = fn())
return result
}
// 调用
let num1 = ref(5)
let num2 = ref(8)
let sum1 = computed(() => num1.value * num2.value)
let sum2 = computed(() => sum1.value * 10)
console.log(sum1.value) // 40
console.log(sum2.value) // 400
num1.value = 10
console.log(sum1.value) // 80
console.log(sum2.value) // 800
num2.value = 16
console.log(sum1.value) // 160
console.log(sum2.value) // 1600