Vue2与Vue3响应式系统对比分析

94 阅读5分钟

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' })
主要特点
  1. 通过Object.defineProperty劫持对象属性
  2. 递归遍历对象所有属性
  3. 数组通过重写数组方法实现响应式

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' // 触发更新
主要特点
  1. 使用Proxy代理整个对象
  2. 懒递归,访问时才递归代理
  3. 可以监听动态添加的属性
  4. 可以监听数组索引和length属性

优缺点对比

Vue2响应式系统

优点:

  • 兼容性好,支持IE9+
  • 代码实现相对简单

缺点:

  • 需要递归遍历对象所有属性,初始化开销大
  • 无法检测对象属性的添加和删除
  • 数组索引变化无法检测
  • 需要使用Vue.set()和Vue.delete()来处理动态属性

Vue3响应式系统

优点:

  • 性能更好,懒递归代理
  • 可以检测对象属性的添加和删除
  • 可以检测数组索引和length的变化
  • 支持Map、Set、WeakMap、WeakSet
  • 代码实现更优雅

缺点:

  • 兼容性相对较差,不支持IE11及以下版本
  • Proxy无法被polyfill

性能对比

  1. 初始化性能

    • Vue2:需要递归遍历对象所有属性,性能开销大
    • Vue3:采用懒递归代理,初始化性能更好
  2. 内存占用

    • Vue2:需要为每个属性创建getter/setter
    • Vue3:仅需要创建一个Proxy对象
  3. 大型数据处理

    • 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)
})

性能优化建议

  1. 大数据列表优化
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 // 一次性更新
  1. 避免不必要的响应式
import { markRaw } from 'vue'

const state = reactive({
    // 标记不需要响应式的大对象
    staticData: markRaw(hugeObject),
    // 需要响应式的数据
    dynamicData: { /* ... */ }
})
  1. 组件优化
// 使用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的响应式系统无疑是更好的选择。