The core idea of a responsive system
Core Concepts of Reactive Systems
Reactive systems are one of the core mechanisms in modern frontend frameworks, capable of automatically tracking data changes and updating related views. Vue 3's reactive system is based on Proxy, offering significant improvements over Vue 2's Object.defineProperty
.
Data Hijacking and Dependency Collection
Vue 3 uses Proxy to intercept object operations, collecting dependencies in getters and triggering updates in setters. This mechanism is more efficient than Vue 2's recursive traversal of object properties.
const reactiveMap = new WeakMap()
function reactive(target) {
const proxy = new Proxy(target, {
get(target, key, receiver) {
track(target, key) // Collect dependencies
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
const oldValue = target[key]
const result = Reflect.set(target, key, value, receiver)
if (oldValue !== value) {
trigger(target, key) // Trigger updates
}
return result
}
})
reactiveMap.set(target, proxy)
return proxy
}
Implementation of Dependency Tracking
Each reactive property is associated with a dependency collection (Dep). When a property is accessed, the currently running effect (side effect) is recorded in the dependency collection.
let activeEffect = null
const targetMap = new WeakMap()
function track(target, key) {
if (!activeEffect) return
let depsMap = targetMap.get(target)
if (!depsMap) {
depsMap = new Map()
targetMap.set(target, depsMap)
}
let dep = depsMap.get(key)
if (!dep) {
dep = new Set()
depsMap.set(key, dep)
}
dep.add(activeEffect)
}
The Update Triggering Process
When reactive data changes, the system looks up the corresponding dependency collection and executes all the side effect functions within it.
function trigger(target, key) {
const depsMap = targetMap.get(target)
if (!depsMap) return
const effects = depsMap.get(key)
if (effects) {
effects.forEach(effect => effect())
}
}
Side Effect Management
Side effects (effects) are the core concept of reactive systems, representing code that responds to data changes. Vue 3 creates and manages side effects through the effect
function.
function effect(fn) {
const effectFn = () => {
cleanup(effectFn)
activeEffect = effectFn
fn()
activeEffect = null
}
effectFn.deps = []
effectFn()
}
function cleanup(effectFn) {
for (const dep of effectFn.deps) {
dep.delete(effectFn)
}
effectFn.deps.length = 0
}
Implementation Differences in Reactive APIs
Vue 3 provides multiple ways to create reactive data, each with different characteristics and use cases.
Differences Between ref
and reactive
function ref(value) {
const refObj = {
get value() {
track(refObj, 'value')
return value
},
set value(newVal) {
if (newVal !== value) {
value = newVal
trigger(refObj, 'value')
}
}
}
return refObj
}
Implementation of shallowReactive
Shallow reactive objects only make top-level properties reactive:
function shallowReactive(target) {
return new Proxy(target, {
get(target, key, receiver) {
if (key === '__v_isReactive') return true
const res = Reflect.get(target, key, receiver)
track(target, key)
return res
},
// setter is the same as reactive
})
}
Optimization Strategies in the Reactive System
Vue 3's reactive system includes multiple performance optimizations to ensure efficiency in complex scenarios.
Optimization of Dependency Collection
Bitwise operations are used to mark dependency relationships, reducing unnecessary dependency collection:
const enum TrackOpTypes {
GET = 'get',
HAS = 'has',
ITERATE = 'iterate'
}
const enum TriggerOpTypes {
SET = 'set',
ADD = 'add',
DELETE = 'delete',
CLEAR = 'clear'
}
Batch Update Mechanism
Microtask queues are used to batch process updates, avoiding redundant rendering:
const queue = []
let isFlushing = false
function queueJob(job) {
if (!queue.includes(job)) {
queue.push(job)
if (!isFlushing) {
isFlushing = true
Promise.resolve().then(flushJobs)
}
}
}
function flushJobs() {
try {
for (let i = 0; i < queue.length; i++) {
queue[i]()
}
} finally {
queue.length = 0
isFlushing = false
}
}
Reactive System and Component Rendering
Changes in reactive data ultimately trigger component re-rendering, a process involving virtual DOM diffing and updates.
Component Update Trigger Mechanism
function setupRenderEffect(instance, vnode, container) {
instance.update = effect(() => {
if (!instance.isMounted) {
// Initial render
const subTree = instance.render()
patch(null, subTree, container)
instance.isMounted = true
} else {
// Update
const nextTree = instance.render()
const prevTree = instance.subTree
patch(prevTree, nextTree, container)
instance.subTree = nextTree
}
}, {
scheduler: queueJob
})
}
Handling Edge Cases in the Reactive System
Various special scenarios must be considered in real-world applications to ensure the stability and reliability of the reactive system.
Handling Circular References
function reactive(target) {
if (reactiveMap.has(target)) {
return reactiveMap.get(target)
}
// ...remaining Proxy creation logic
}
Reactive Conversion of Primitive Values
function toReactive(value) {
return isObject(value) ? reactive(value) : value
}
Debugging Support in the Reactive System
In development environments, detailed debugging information is provided to help developers understand reactive behavior.
function track(target, key) {
if (__DEV__ && !activeEffect) {
console.warn(`Property "${String(key)}" was accessed without an active effect`)
}
// ...normal track logic
}
Integration of the Reactive System with TypeScript
Vue 3's reactive system is entirely written in TypeScript, offering comprehensive type support.
interface ReactiveEffect<T = any> {
(): T
_isEffect: true
id: number
active: boolean
raw: () => T
deps: Array<Dep>
options: ReactiveEffectOptions
}
interface ReactiveEffectOptions {
lazy?: boolean
scheduler?: (job: ReactiveEffect) => void
onTrack?: (event: DebuggerEvent) => void
onTrigger?: (event: DebuggerEvent) => void
onStop?: () => void
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:Monorepo代码组织方式
下一篇:编译与运行时的分离设计