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
函数管理副作用,确保数据变化时视图自动更新。