Vue2与Vue3响应式系统对比分析
实现原理
Vue2响应式系统
核心实现:Object.defineProperty
// 依赖收集类
class Dep {
constructor() {
this.subscribers = new Set()
}
depend() {
if (activeEffect) {
this.subscribers.add(activeEffect)
}
}
notify() {
this.subscribers.forEach(effect => effect())
}
}
let activeEffect = null
// 响应式处理核心函数
function defineReactive(obj, key, val) {
// 如果val是对象,需要递归处理
if (typeof val === 'object') {
observe(val)
}
const dep = new Dep()
Object.defineProperty(obj, key, {
get() {
dep.depend() // 收集依赖
return val
},
set(newVal) {
if (val === newVal) return
val = newVal
// 如果新值是对象,也需要做响应式处理
if (typeof newVal === 'object') {
observe(newVal)
}
dep.notify() // 通知更新
}
})
}
// 遍历对象的所有属性,进行响应式处理
function observe(obj) {
if (!obj || typeof obj !== 'object') return
Object.keys(obj).forEach(key => {
defineReactive(obj, key, obj[key])
})
}
// 数组方法重写示例
const arrayProto = Array.prototype
const arrayMethods = Object.create(arrayProto)
['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(method => {
const original = arrayProto[method]
Object.defineProperty(arrayMethods, method, {
value: function mutator(...args) {
const result = original.apply(this, args)
const ob = this.__ob__
ob.dep.notify()
return result
}
})
})
// 使用示例
const data = {
message: 'Hello',
list: [1, 2, 3]
}
observe(data)
// 创建响应式效果
activeEffect = () => {
console.log('数据更新了:', data.message)
}
data.message // 访问属性,触发依赖收集
// 修改数据,触发更新
data.message = 'Hello Vue2'
Vue2响应式处理数组的特殊处理
// 数组响应式处理示例
function observeArray(arr) {
arr.__proto__ = arrayMethods // 替换数组原型方法
arr.forEach(item => observe(item)) // 递归处理数组元素
}
// 使用Vue.set处理数组
function set(target, key, val) {
if (Array.isArray(target)) {
target.splice(key, 1, val)
return val
}
// 对象的处理...
}
// 实际应用示例
const list = [{ name: 'item1' }, { name: 'item2' }]
observeArray(list)
// 以下操作都能触发响应式更新
list.push({ name: 'item3' })
list[0].name = 'updated item1'
set(list, 1, { name: 'new item2' })
主要特点
- 通过Object.defineProperty劫持对象属性
- 递归遍历对象所有属性
- 数组通过重写数组方法实现响应式
Vue3响应式系统
核心实现:Proxy
// 依赖收集相关
let activeEffect = null
const targetMap = new WeakMap()
// 依赖收集
function track(target, key) {
if (!activeEffect) return
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 dep = depsMap.get(key)
if (dep) {
dep.forEach(effect => effect())
}
}
// 创建响应式对象
function reactive(target) {
if (!target || typeof target !== 'object') return target
const proxy = new Proxy(target, {
get(target, key, receiver) {
const res = Reflect.get(target, key, receiver)
track(target, key) // 依赖收集
// 深层响应式处理
return typeof res === 'object' ? reactive(res) : res
},
set(target, key, value, receiver) {
const oldValue = target[key]
const result = Reflect.set(target, key, value, receiver)
if (oldValue !== value) {
trigger(target, key) // 触发更新
}
return result
},
deleteProperty(target, key) {
const hadKey = key in target
const result = Reflect.deleteProperty(target, key)
if (hadKey && result) {
trigger(target, key) // 触发更新
}
return result
}
})
return proxy
}
// ref实现示例
function ref(value) {
const refObject = {
get value() {
track(refObject, 'value')
return value
},
set value(newValue) {
if (value === newValue) return
value = newValue
trigger(refObject, 'value')
}
}
return refObject
}
// 实际应用示例
const state = reactive({
count: 0,
list: [1, 2, 3],
user: {
name: 'Vue3',
age: 3
}
})
// 创建响应式效果
activeEffect = () => {
console.log('状态更新:', state.count, state.user.name)
}
// 触发响应式更新
state.count++
state.user.name = 'Vue3.2'
state.list.push(4) // 数组方法自动触发更新
// ref的使用示例
const message = ref('Hello')
activeEffect = () => console.log(message.value)
message.value = 'Hello Vue3' // 触发更新
主要特点
- 使用Proxy代理整个对象
- 懒递归,访问时才递归代理
- 可以监听动态添加的属性
- 可以监听数组索引和length属性
优缺点对比
Vue2响应式系统
优点:
- 兼容性好,支持IE9+
- 代码实现相对简单
缺点:
- 需要递归遍历对象所有属性,初始化开销大
- 无法检测对象属性的添加和删除
- 数组索引变化无法检测
- 需要使用Vue.set()和Vue.delete()来处理动态属性
Vue3响应式系统
优点:
- 性能更好,懒递归代理
- 可以检测对象属性的添加和删除
- 可以检测数组索引和length的变化
- 支持Map、Set、WeakMap、WeakSet
- 代码实现更优雅
缺点:
- 兼容性相对较差,不支持IE11及以下版本
- Proxy无法被polyfill
性能对比
-
初始化性能
- Vue2:需要递归遍历对象所有属性,性能开销大
- Vue3:采用懒递归代理,初始化性能更好
-
内存占用
- Vue2:需要为每个属性创建getter/setter
- Vue3:仅需要创建一个Proxy对象
-
大型数据处理
- Vue2:数据量大时初始化慢,内存占用高
- Vue3:数据量大时表现更好,按需代理
最佳实践
Vue2最佳实践
// 1. 提前声明所有响应式属性
export default {
data() {
return {
// 提前声明所有属性
user: {
name: '',
age: 0,
settings: {
theme: 'light',
notifications: true
}
},
list: []
}
}
}
// 2. 使用Vue.set()添加新属性
Vue.set(this.user, 'email', 'example@mail.com')
// 3. 避免大量深层次数据,扁平化数据结构
const goodStructure = {
users: {
'user1': { name: 'User1' },
'user2': { name: 'User2' }
},
userPosts: {
'user1': ['post1', 'post2'],
'user2': ['post3']
}
}
Vue3最佳实践
// 1. 充分利用reactive和ref
import { ref, reactive, computed } from 'vue'
// 简单值用ref
const count = ref(0)
const message = ref('Hello')
// 复杂对象用reactive
const state = reactive({
user: {
name: 'Vue3',
settings: { theme: 'dark' }
},
posts: []
})
// 2. 使用shallowReactive处理大数据
import { shallowReactive } from 'vue'
const bigData = shallowReactive({
list: new Array(10000).fill(0).map((_, i) => ({ id: i })),
metadata: {
// 只有第一层是响应式的
total: 10000,
currentPage: 1
}
})
// 3. 合理使用readonly提高性能
import { readonly } from 'vue'
const original = reactive({ count: 0 })
const copy = readonly(original) // 创建只读副本
// 4. 使用computed缓存计算结果
const filteredList = computed(() => {
return state.posts.filter(post => post.published)
})
性能优化建议
- 大数据列表优化
import { shallowRef } from 'vue'
// 使用shallowRef处理大型列表
const list = shallowRef([])
// 批量更新
let newList = [...list.value]
for (let i = 0; i < 1000; i++) {
newList.push({ id: i })
}
list.value = newList // 一次性更新
- 避免不必要的响应式
import { markRaw } from 'vue'
const state = reactive({
// 标记不需要响应式的大对象
staticData: markRaw(hugeObject),
// 需要响应式的数据
dynamicData: { /* ... */ }
})
- 组件优化
// 使用v-once处理静态内容
<template>
<div v-once>静态内容</div>
<div>{{ dynamicData }}</div>
</template>
// 使用v-memo缓存条件渲染
<template>
<div v-memo="[item.id, item.active]">
{{ item.name }}
</div>
</template>
总结
Vue3的响应式系统是对Vue2的一次重大升级,通过使用Proxy代替Object.defineProperty,解决了Vue2中存在的诸多限制,提供了更好的性能和更完善的响应式能力。虽然在兼容性方面有所牺牲,但在现代浏览器环境下,Vue3的响应式系统无疑是更好的选择。