开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情
Vue3 Reactive源码解析
在Vue3中,响应式系统被完全重写,使用了一个新的API:reactive。本文将对其源代码进行解析。
什么是响应式?
在Vue中,响应式是指当数据发生变化时,界面会自动更新反映这些变化。这种机制是通过Vue的响应式系统实现的。
Vue3的响应式系统
在Vue3中,我们使用reactive函数来创建响应式对象。该函数接受一个普通对象作为参数,并将其转换为响应式对象。
import { reactive } from 'vue'
const state = reactive({
count: 0
})
reactive函数的实现
我们来看一下reactive函数的实现。下面是它的简化版本:
function reactive(obj) {
// ...
return new Proxy(obj, {
// ...
})
}
reactive函数接受一个普通对象作为参数,并返回一个代理对象。代理对象会拦截对原始对象的访问,并在必要时触发更新。
Proxy
在Vue3的响应式系统中,Proxy扮演了一个非常重要的角色。它是ES6中的一个新特性,可以拦截对象的访问并做出响应。
下面是一个例子,演示了如何使用Proxy来拦截对象的访问:
const obj = {
name: 'Alice',
age: 20
}
const proxy = new Proxy(obj, {
get(target, key, receiver) {
console.log(`Getting ${key}`)
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
console.log(`Setting ${key} to ${value}`)
return Reflect.set(target, key, value, receiver)
}
})
console.log(proxy.name) // Getting name, Alice
proxy.age = 21 // Setting age to 21
在上面的例子中,我们创建了一个代理对象proxy,它会拦截对原始对象obj的访问。当我们访问proxy的属性时,会触发get方法。当我们设置proxy的属性时,会触发set方法。
Reactive对象的代理
在Vue3中,我们通过reactive函数创建的对象是一个代理对象。这个代理对象会拦截对原始对象的访问,并在必要时触发更新。
下面是一个简化版的reactive函数:
function reactive(obj) {
const observed = new Proxy(obj, {
get(target, key, receiver) {
// ...
},
set(target, key, value, receiver) {
// ...
}
})
return observed
}
reactive函数返回一个代理对象observed,它会拦截对原始对象obj的访问。这个代理对象有两个拦截器:get和set。
get拦截器
当我们访问代理对象的属性时,get拦截器会被触发。下面是一个简化版的get拦截器:
get(target, key, receiver) {
const value = Reflect.get(target, key, receiver)
track(target, 'get', key)
return value
}
get拦截器会调用Reflect.get方法来获取原始对象上的值。然后,它会调用track函数来记录这个属性被访问了。
set拦截器
当我们设置代理对象的属性时,set拦截器会被触发。下面是一个简化版的set拦截器:
set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver)
trigger(target, 'set', key, value)
return result
}
set拦截器会调用Reflect.set方法来设置原始对象上的值。然后,它会调用trigger函数来触发更新。
track函数
track函数用于记录属性的访问。它会将属性和依赖关系存储在一个全局的Map对象中。
const targetMap = new WeakMap()
function track(target, type, key) {
// 获取当前的响应式对象
const currentEffect = activeEffectStack[activeEffectStack.length - 1]
if (currentEffect) {
// 获取target对象的Map
let depsMap = targetMap.get(target)
if (!depsMap) {
// 如果Map不存在,则创建一个新的Map
depsMap = new Map()
targetMap.set(target, depsMap)
}
// 获取key对应的Set
let dep = depsMap.get(key)
if (!dep) {
// 如果Set不存在,则创建一个新的Set
dep = new Set()
depsMap.set(key, dep)
}
// 将当前的effect添加到Set中
dep.add(currentEffect)
}
}
trigger函数
trigger函数用于触发更新。它会获取属性的依赖关系,并执行它们对应的函数。
function trigger(target, type, key, value) {
// 获取target对象的Map
const depsMap = targetMap.get(target)
if (!depsMap) {
// 如果Map不存在,则说明没有依赖关系需要更新
return
}
// 获取key对应的Set
const dep = depsMap.get(key)
if (dep) {
// 执行Set中所有effect的函数
dep.forEach(effect => {
effect()
})
}
}
总结
在Vue3中,响应式系统使用了Proxy和Map等新特性来实现。通过reactive函数创建的对象是一个代理对象,它会拦截对原始对象的访问,并在必要时触发更新。track函数用于记录属性的依赖关系,trigger函数用于触发更新。