阿里云主机折上折
  • 微信号
Current Site:Index > Performance optimization techniques for responsive systems

Performance optimization techniques for responsive systems

Author:Chuan Chen 阅读数:55585人阅读 分类: Vue.js

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

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.