Performance optimization techniques for responsive systems
Core Mechanisms of the Reactive System
Vue3's reactive system is based on Proxy, offering significant improvements over Vue2's Object.defineProperty. The core lies in the track
and trigger
functions, which establish dependency collection and update triggering mechanisms:
const targetMap = new WeakMap()
function track(target, key) {
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)
}
function trigger(target, key) {
const depsMap = targetMap.get(target)
if (!depsMap) return
const effects = depsMap.get(key)
effects && effects.forEach(effect => effect())
}
Optimizations in Reactive Data Structures
Flattening Reactive Objects
Vue3 optimizes nested object handling by avoiding recursive conversion of all properties:
const obj = reactive({
nested: {
a: 1
}
})
// Vue2 would recursively convert the nested object
// Vue3 only converts nested when accessed
console.log(obj.nested) // Only now is the nested Proxy created
Primitive Value Wrapping
Primitive values are wrapped with ref
to avoid unnecessary reactive overhead:
const count = ref(0) // Creates a reactive reference with a value property
// Simplified internal implementation
function ref(value) {
return {
get value() {
track(this, 'value')
return value
},
set value(newVal) {
value = newVal
trigger(this, 'value')
}
}
}
Dependency Collection Optimization Strategies
Lazy Dependency Tracking
Vue3 only collects dependencies actually used during the first run of an effect:
const obj = reactive({ a: 1, b: 2 })
effect(() => {
// Only collects dependencies when obj.a is accessed
console.log(obj.a ? obj.b : 0)
})
// Modifying unaccessed properties won't trigger the effect
obj.c = 3 // No effect
Dependency Relationship Caching
The currently executing effect is cached via effectStack
to avoid duplicate collection:
let effectStack = []
function effect(fn) {
const e = () => {
if (effectStack.includes(e)) return
try {
effectStack.push(e)
return fn()
} finally {
effectStack.pop()
}
}
e()
return e
}
Batch Updates and Scheduling
Asynchronous Update Queue
Batch updates are implemented via nextTick
to reduce unnecessary DOM operations:
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() {
queue.sort((a, b) => a.id - b.id)
for (let i = 0; i < queue.length; i++) {
queue[i]()
}
queue.length = 0
isFlushing = false
}
Custom Schedulers
Allows customizing update triggering logic:
effect(() => {
console.log(obj.a)
}, {
scheduler(effect) {
// Control update timing
requestIdleCallback(effect)
}
})
Compile-Time Optimizations
PatchFlag Markers
The compiler analyzes templates to generate optimization hints:
<div>
<div :class="{ active: isActive }"></div>
<div>{{ dynamicText }}</div>
</div>
The compiled render function includes PatchFlags:
function render() {
return (_openBlock(), _createBlock("div", null, [
_createVNode("div", { class: _normalizeClass({ active: _ctx.isActive }) }, null, 2 /* CLASS */),
_createVNode("div", null, _toDisplayString(_ctx.dynamicText), 1 /* TEXT */)
]))
}
Static Hoisting
Static nodes are hoisted outside the render function:
const _hoisted_1 = _createVNode("div", null, "static content")
function render() {
return (_openBlock(), _createBlock("div", null, [
_hoisted_1,
_createVNode("div", null, _ctx.dynamicContent)
]))
}
Fine-Grained Control of Reactive APIs
shallowReactive
Creates a shallow reactive object:
const shallow = shallowReactive({
nested: { a: 1 }
})
// Only shallow itself is reactive
shallow.nested.a = 2 // Won't trigger updates
markRaw
Marks an object to never be made reactive:
const foo = markRaw({})
const obj = reactive({ foo }) // obj.foo won't be proxied
Memory Management Optimizations
WeakMap Usage
Uses WeakMap to store dependency relationships, preventing memory leaks:
const targetMap = new WeakMap() // Keys are ordinary objects and won't prevent garbage collection
function getDeps(target) {
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
return depsMap
}
Reactive Object Caching
Returns the same Proxy for the same raw object:
const reactiveMap = new WeakMap()
function reactive(target) {
let proxy = reactiveMap.get(target)
if (!proxy) {
proxy = new Proxy(target, handlers)
reactiveMap.set(target, proxy)
}
return proxy
}
Performance Monitoring and Debugging
Reactive Tracing
In development mode, property access can be traced:
function track(target, key) {
if (!activeEffect || !shouldTrack) return
// ...
if (__DEV__) {
debugger // Debugging logic can be added here
}
}
Performance Markers
Uses the Performance API to measure critical operation durations:
function trigger(target, key) {
const start = performance.now()
// ...Trigger logic
const duration = performance.now() - start
if (duration > 10) {
console.warn(`Long trigger duration: ${duration}ms`)
}
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
下一篇:手动停止响应的方法