Comparison with Vue 2's reactivity system
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:
Proxy
can detect newly added properties (Vue 2 requiresVue.set
).Proxy
can intercept array index modifications andlength
changes.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'sdata
reactive
vs. Vue 2'sdata
objectstoRefs
for destructuring reactive objectsshallowReactive
for shallow reactivity
Performance Optimizations
- Lazy Reactivity: Vue 3 only establishes dependencies when properties are actually accessed.
- Caching: Repeated access to the same property doesn't repeatedly trigger getters.
- Batch Updates: More efficient asynchronous queue optimizations.
- 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
上一篇:响应式系统边界情况处理
下一篇:虚拟DOM的数据结构设计