阿里云主机折上折
  • 微信号
Current Site:Index > Comparison with Vue 2's reactivity system

Comparison with Vue 2's reactivity system

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

Vue 3's reactivity system has undergone a comprehensive refactor compared to Vue 2, with the core change being the replacement of Object.defineProperty with Proxy for data interception. This shift brings performance improvements and enhanced functionality, along with some behavioral differences. Below is a detailed comparison across three aspects: implementation principles, API changes, and practical scenarios.

Differences in Underlying Implementation of Reactivity

Vue 2 uses Object.defineProperty to recursively traverse data objects, converting each property into getters/setters:

// Simplified implementation of Vue 2 reactivity
function defineReactive(obj, key) {
  let value = obj[key]
  Object.defineProperty(obj, key, {
    get() {
      console.log(`Reading ${key}: ${value}`)
      return value
    },
    set(newVal) {
      console.log(`Setting ${key}: ${newVal}`)
      value = newVal
    }
  })
}

Vue 3 adopts Proxy to intercept operations on the entire object:

// Simplified implementation of Vue 3 reactivity
function reactive(obj) {
  return new Proxy(obj, {
    get(target, key) {
      console.log(`Reading ${key}: ${target[key]}`)
      return target[key]
    },
    set(target, key, value) {
      console.log(`Setting ${key}: ${value}`)
      target[key] = value
      return true
    }
  })
}

This change introduces several key differences:

  1. Proxy can detect newly added properties (Vue 2 requires Vue.set).
  2. Proxy can intercept array index modifications and length changes.
  3. Proxy returns a new object, leaving the original object unmodified.

Improvements in Array Handling

Vue 2 requires special handling for arrays by overriding array methods:

// Vue 2 array reactivity handling
const arrayProto = Array.prototype
const arrayMethods = Object.create(arrayProto)
;['push', 'pop', 'shift', 'unshift'].forEach(method => {
  const original = arrayProto[method]
  arrayMethods[method] = function(...args) {
    const result = original.apply(this, args)
    console.log('Array change notification')
    return result
  }
})

Vue 3's Proxy natively supports intercepting array operations:

const arr = reactive([1, 2, 3])
arr.push(4) // Directly triggers reactivity
arr[0] = 5  // Directly triggers reactivity

Changes in Reactivity APIs

Vue 2 primarily declares reactive data through the data option:

// Vue 2 approach
export default {
  data() {
    return {
      count: 0
    }
  }
}

Vue 3 provides more flexible APIs:

// Vue 3 Composition API
import { ref, reactive } from 'vue'

export default {
  setup() {
    const count = ref(0)
    const state = reactive({
      message: 'Hello'
    })
    
    return { count, state }
  }
}

Key API comparisons:

  • ref vs. Vue 2's data
  • reactive vs. Vue 2's data objects
  • toRefs for destructuring reactive objects
  • shallowReactive for shallow reactivity

Performance Optimizations

  1. Lazy Reactivity: Vue 3 only establishes dependencies when properties are actually accessed.
  2. Caching: Repeated access to the same property doesn't repeatedly trigger getters.
  3. Batch Updates: More efficient asynchronous queue optimizations.
  4. Tree-shaking Support: Unused reactivity APIs are removed.

Example comparison:

// Vue 2 recursively converts all properties immediately
const data = {
  deep: { a: 1, b: 2 } // Immediately made reactive
}

// Vue 3 converts on demand
const proxy = reactive({
  deep: { a: 1, b: 2 } // Only converts internals when `deep` is accessed
})

Changes in Dependency Collection

Vue 2's dependency collection is implemented via the Dep class:

// Vue 2 dependency collection
class Dep {
  constructor() {
    this.subs = []
  }
  depend() {
    if (Dep.target) {
      this.subs.push(Dep.target)
    }
  }
  notify() {
    this.subs.forEach(sub => sub.update())
  }
}

Vue 3 adopts a more granular effect tracking system:

// Vue 3 effect system
let activeEffect

class ReactiveEffect {
  constructor(fn) {
    this.fn = fn
  }
  run() {
    activeEffect = this
    return this.fn()
  }
}

function effect(fn) {
  const _effect = new ReactiveEffect(fn)
  _effect.run()
}

Expanded Reactivity Scope

Vue 3 can react to more JavaScript operations:

  • Collection types like Map/Set
  • Property addition/deletion
  • Array index modifications
  • Object prototype chain access
const map = reactive(new Map())
map.set('key', 'value') // Triggers reactivity

const obj = reactive({})
obj.newProp = 'test' // Directly reactive

Enhanced Debugging Capabilities

Vue 3 provides better debugging support for reactivity:

import { onTrack, onTrigger } from 'vue'

const obj = reactive({
  foo: 1
}, {
  onTrack(e) {
    console.log('Tracking dependency:', e)
  },
  onTrigger(e) {
    console.log('Triggering update:', e)
  }
})

TypeScript Integration

Vue 3's reactivity system offers better TypeScript support:

interface User {
  name: string
  age: number
}

const user = reactive<User>({
  name: 'Alice',
  age: 25
})

user.age++ // Type-checking works
user.name = 123 // Type error

Comparison of Reactivity Utility Functions

Vue 2 and Vue 3 provide some reactivity utility functions, but with differences:

Feature Vue 2 Vue 3
Create reactive object Vue.observable() reactive()
Create reference None ref()
Remove reactivity None markRaw()
Convert references None toRef(), toRefs()

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱: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 ☕.