vue3数据双向绑定解析
Vue 3 的双向绑定原理主要基于 Proxy 和 Reflect,核心源码在 reactivity 模块中。
1. 核心模块:reactivity
reactivity 模块负责响应式数据的实现,主要包括以下几个文件:
reactive.ts:处理对象和数组的响应式。ref.ts:处理基本类型的响应式。effect.ts:管理副作用和依赖追踪。
2. reactive.ts
reactive.ts 中的 reactive 函数将普通对象转换为响应式对象,核心代码如下:
export function reactive<T extends object>(target: T): T {
if (target && (target as any).__v_isReadonly) {
return target
}
return createReactiveObject(
target,
false,
mutableHandlers,
mutableCollectionHandlers
)
}
function createReactiveObject(
target: Target,
isReadonly: boolean,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any>
) {
if (!isObject(target)) {
return target
}
const proxy = new Proxy(target, baseHandlers)
return proxy
}
reactive 函数通过 Proxy 包装目标对象,mutableHandlers 定义了 get 和 set 等拦截操作。
3. mutableHandlers
mutableHandlers 定义了 Proxy 的拦截行为,核心代码如下:
export const mutableHandlers: ProxyHandler<object> = {
get(target: Target, key: string | symbol, receiver: object) {
const res = Reflect.get(target, key, receiver)
track(target, key)
return isObject(res) ? reactive(res) : res
},
set(target: Target, key: string | symbol, value: any, receiver: object) {
const oldValue = (target as any)[key]
const result = Reflect.set(target, key, value, receiver)
if (hasChanged(value, oldValue)) {
trigger(target, key)
}
return result
}
}
get:在访问属性时调用track追踪依赖。set:在修改属性时调用trigger触发更新。
4. effect.ts
effect.ts 负责管理副作用和依赖追踪,核心代码如下:
export function effect<T = any>(
fn: () => T,
options: ReactiveEffectOptions = EMPTY_OBJ
): ReactiveEffect<T> {
const effect = createReactiveEffect(fn, options)
if (!options.lazy) {
effect()
}
return effect
}
function createReactiveEffect<T = any>(
fn: () => T,
options: ReactiveEffectOptions
): ReactiveEffect<T> {
const effect = function reactiveEffect(): unknown {
if (!effect.active) {
return options.scheduler ? undefined : fn()
}
if (!effectStack.includes(effect)) {
cleanup(effect)
try {
enableTracking()
effectStack.push(effect)
activeEffect = effect
return fn()
} finally {
effectStack.pop()
resetTracking()
activeEffect = effectStack[effectStack.length - 1]
}
}
} as ReactiveEffect
effect.id = uid++
effect._isEffect = true
effect.active = true
effect.raw = fn
effect.deps = []
effect.options = options
return effect
}
effect 函数创建并执行副作用函数,track 和 trigger 分别用于依赖追踪和触发更新。
5. track 和 trigger
track 和 trigger 是依赖追踪和更新的核心:
export function track(target: object, key: unknown) {
if (!shouldTrack || activeEffect === undefined) {
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()))
}
if (!dep.has(activeEffect)) {
dep.add(activeEffect)
activeEffect.deps.push(dep)
}
}
export function trigger(
target: object,
key?: unknown,
newValue?: unknown,
oldValue?: unknown,
oldTarget?: Map<unknown, unknown> | Set<unknown>
) {
const depsMap = targetMap.get(target)
if (!depsMap) {
return
}
const effects = new Set<ReactiveEffect>()
const add = (effectsToAdd: Set<ReactiveEffect> | undefined) => {
if (effectsToAdd) {
effectsToAdd.forEach(effect => {
if (effect !== activeEffect || effect.allowRecurse) {
effects.add(effect)
}
})
}
}
if (key !== void 0) {
add(depsMap.get(key))
}
const run = (effect: ReactiveEffect) => {
if (effect.options.scheduler) {
effect.options.scheduler(effect)
} else {
effect()
}
}
effects.forEach(run)
}
track:将当前副作用函数添加到依赖集合。trigger:遍历依赖集合并执行副作用函数。
Vue 3 的双向绑定通过 Proxy 拦截对象操作,结合 track 和 trigger 实现依赖追踪和更新触发。effect 函数管理副作用,确保数据变化时视图自动更新。
