Appearance
Vue 3 与 Vue 2 全面对比
1. 响应式系统
1.1 Vue 2 响应式原理
Vue 2 使用 Object.defineProperty 实现响应式系统。
javascript
function defineReactive(obj, key, val) {
const dep = new Dep()
Object.defineProperty(obj, key, {
get() {
Dep.target && dep.addSub(Dep.target)
return val
},
set(newVal) {
if (newVal !== val) {
val = newVal
dep.notify()
}
}
})
}局限性:
- 无法监听对象新增属性
- 无法监听数组索引和长度变化
- 需要深度遍历对象,性能较差
1.2 Vue 3 响应式原理
Vue 3 使用 Proxy 实现响应式系统。
javascript
function reactive(target) {
return new Proxy(target, {
get(target, key, receiver) {
const result = Reflect.get(target, key, receiver)
// 依赖收集
track(target, key)
// 递归处理嵌套对象
if (isObject(result)) {
return reactive(result)
}
return result
},
set(target, key, value, receiver) {
const oldValue = Reflect.get(target, key, receiver)
const result = Reflect.set(target, key, value, receiver)
// 触发更新
if (oldValue !== value) {
trigger(target, key)
}
return result
},
deleteProperty(target, key) {
const existed = Reflect.has(target, key)
const result = Reflect.deleteProperty(target, key)
if (existed) {
trigger(target, key, 'delete')
}
return result
}
})
}优势:
- 可以监听对象新增属性
- 可以监听数组索引和长度变化
- 不需要深度遍历,性能更好
- 支持 Map、Set 等数据结构
2. 虚拟 DOM
2.1 Vue 2 虚拟 DOM
Vue 2 的虚拟 DOM 是基于 VNode 类实现的。
javascript
class VNode {
constructor(tag, data, children, text, elm) {
this.tag = tag
this.data = data
this.children = children
this.text = text
this.elm = elm
}
}特点:
- 每个 VNode 是一个对象,包含标签、数据、子节点等信息
- 渲染函数返回 VNode 树
- 通过 diff 算法比较新旧 VNode 树的差异
2.2 Vue 3 虚拟 DOM
Vue 3 对虚拟 DOM 进行了重构,使用了更高效的结构。
改进:
- 优化了 VNode 结构,减少了内存占用
- 引入了 Fragment,支持多个根节点
- 引入了 Teleport,支持组件内容的转移
- 引入了 Suspense,支持异步组件的处理
3. Diff 算法
3.1 Vue 2 Diff 算法
Vue 2 使用双端比较算法。
javascript
function updateChildren(parentElm, oldCh, newCh) {
let oldStartIdx = 0,
newStartIdx = 0
let oldEndIdx = oldCh.length - 1,
newEndIdx = newCh.length - 1
let oldStartVnode = oldCh[0],
newStartVnode = newCh[0]
let oldEndVnode = oldCh[oldEndIdx],
newEndVnode = newCh[newEndIdx]
let oldKeyToIdx, idxInOld, vnodeToMove, refElm
// 双端比较逻辑
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
// 省略比较逻辑...
}
// 处理剩余节点
if (oldStartIdx > oldEndIdx) {
// 添加新节点
} else if (newStartIdx > newEndIdx) {
// 删除旧节点
}
}局限性:
- 对于长列表,diff 算法的时间复杂度仍然较高
- 无法利用静态节点的优势
3.2 Vue 3 Diff 算法
Vue 3 对 Diff 算法进行了优化,引入了静态提升、补丁标志等技术。
改进:
- 静态提升:将静态节点提升到渲染函数外部,避免重复创建
- 补丁标志:为 VNode 添加补丁标志,只更新需要变化的部分
- 最长递增子序列:使用最长递增子序列算法优化列表更新
- 缓存事件处理函数:避免每次渲染都创建新的事件处理函数
javascript
// 编译后的代码示例
function render() {
return (
_openBlock(),
_createElementBlock(
_Fragment,
null,
[
_createElementVNode('div', null, 'Hello'), // 静态节点
_createElementVNode(
'div',
null,
_toDisplayString(_ctx.count),
1 /* TEXT */
) // 只更新文本
],
64 /* STABLE_FRAGMENT */
)
)
}4. 编译优化
4.1 Vue 2 编译
Vue 2 的编译过程包括:
- 模板解析:将模板解析为 AST
- 优化:标记静态节点
- 代码生成:生成渲染函数
局限性:
- 编译优化有限,运行时仍需进行较多计算
- 无法充分利用静态节点的优势
4.2 Vue 3 编译优化
Vue 3 引入了更先进的编译优化技术:
编译时优化:
- 静态提升:将静态节点和静态属性提升到渲染函数外部
- 补丁标志:为动态节点添加补丁标志,只更新需要变化的部分
- 缓存事件处理函数:缓存事件处理函数,避免每次渲染都创建新函数
- 内联模板:对于简单模板,直接内联渲染逻辑
运行时优化:
- 块树:将模板分割为多个块,只更新变化的块
- 动态指令参数:优化动态指令参数的处理
- v-for 优化:优化 v-for 指令的处理
5. 组合式 API vs 选项式 API
5.1 Vue 2 选项式 API
Vue 2 使用选项式 API,将代码按功能分为 data、methods、computed 等选项。
javascript
export default {
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
}
},
computed: {
doubleCount() {
return this.count * 2
}
},
watch: {
count(newVal) {
console.log('Count changed:', newVal)
}
}
}局限性:
- 代码组织按功能划分,逻辑分散
- 难以复用逻辑
- 类型推导困难
5.2 Vue 3 组合式 API
Vue 3 引入了组合式 API,允许按逻辑关注点组织代码。
javascript
import { ref, computed, watch } from 'vue'
export default {
setup() {
const count = ref(0)
const increment = () => {
count.value++
}
const doubleCount = computed(() => count.value * 2)
watch(count, newVal => {
console.log('Count changed:', newVal)
})
return {
count,
increment,
doubleCount
}
}
}优势:
- 按逻辑关注点组织代码,逻辑集中
- 易于复用逻辑
- 更好的类型推导
- 更灵活的代码组织
6. 性能优化
6.1 Vue 2 性能优化
Vue 2 的性能优化主要依赖于:
- 使用 v-if 和 v-show 合理切换
- 使用 key 提高列表渲染性能
- 使用 computed 缓存计算结果
- 使用 watch 监听变化
- 合理使用 keep-alive
6.2 Vue 3 性能优化
Vue 3 在性能方面进行了全面优化:
编译时优化:
- 静态提升
- 补丁标志
- 缓存事件处理函数
运行时优化:
- 响应式系统优化(使用 Proxy)
- Diff 算法优化
- 块树优化
- 内存使用优化
具体数据:
- 渲染性能提升 1.3-2 倍
- 内存使用减少 50%
- 打包体积减少 10-20%
7. TypeScript 支持
7.1 Vue 2 TypeScript 支持
Vue 2 的 TypeScript 支持有限:
- 需要手动添加类型定义
- 类型推导不友好
- 组件选项类型定义复杂
7.2 Vue 3 TypeScript 支持
Vue 3 内置了 TypeScript 支持:
- 完全重写了类型定义
- 更好的类型推导
- 组合式 API 天然支持 TypeScript
- 模板中的类型检查
typescript
// Vue 3 TypeScript 示例
import { defineComponent, ref, computed } from 'vue'
export default defineComponent({
setup() {
const count = ref<number>(0)
const doubleCount = computed<number>(() => count.value * 2)
const increment = (): void => {
count.value++
}
return {
count,
doubleCount,
increment
}
}
})8. 生命周期
8.1 Vue 2 生命周期
Vue 2 的生命周期钩子:
- beforeCreate
- created
- beforeMount
- mounted
- beforeUpdate
- updated
- beforeDestroy
- destroyed
- activated (keep-alive 专用)
- deactivated (keep-alive 专用)
8.2 Vue 3 生命周期
Vue 3 的生命周期钩子:
- setup (替代 beforeCreate 和 created)
- onBeforeMount (替代 beforeMount)
- onMounted (替代 mounted)
- onBeforeUpdate (替代 beforeUpdate)
- onUpdated (替代 updated)
- onBeforeUnmount (替代 beforeDestroy)
- onUnmounted (替代 destroyed)
- onActivated (替代 activated)
- onDeactivated (替代 deactivated)
- onErrorCaptured (替代 errorCaptured)
- onRenderTracked
- onRenderTriggered
使用方式:
javascript
import { onMounted, onUpdated, onUnmounted } from 'vue'
export default {
setup() {
onMounted(() => {
console.log('Component mounted')
})
onUpdated(() => {
console.log('Component updated')
})
onUnmounted(() => {
console.log('Component unmounted')
})
}
}9. 其他新特性
9.1 Vue 3 新特性
- Fragment:支持多个根节点
vue
<template>
<div>First root</div>
<div>Second root</div>
</template>- Teleport:支持组件内容的转移
vue
<template>
<Teleport to="#modal">
<div class="modal">Modal content</div>
</Teleport>
</template>- Suspense:支持异步组件的处理
vue
<template>
<Suspense>
<template #default>
<AsyncComponent />
</template>
<template #fallback>
<div>Loading...</div>
</template>
</Suspense>
</template>- Composition API:组合式 API
- 更好的 TypeScript 支持
- 新的响应式 API:ref、reactive、computed、watch 等
- 新的编译优化:静态提升、补丁标志等
- 更小的打包体积
10. 迁移策略
10.1 从 Vue 2 迁移到 Vue 3
步骤:
- 升级 Vue 3 和相关依赖
- 替换 Vue 2 的 API 为 Vue 3 的 API
- 重构选项式 API 为组合式 API(可选)
- 修复兼容性问题
工具:
- Vue Migration Build:提供兼容层,允许渐进式迁移
- vue-codemod:自动转换代码
10.2 兼容性问题
需要注意的兼容性问题:
- 全局 API 变更:Vue 3 中的全局 API 被移到了 createApp 返回的实例上
- 过滤器移除:Vue 3 移除了过滤器,建议使用计算属性或方法替代
- 指令钩子变更:Vue 3 中的指令钩子发生了变化
- 事件 API 变更:Vue 3 中的事件 API 发生了变化
11. 代码示例对比
11.1 响应式系统对比
Vue 2:
javascript
// Vue 2 响应式
const vm = new Vue({
data: {
user: {
name: 'John',
age: 30
},
items: [1, 2, 3]
}
})
// 无法监听新增属性
vm.user.email = 'john@example.com' // 不会触发更新
// 无法监听数组索引变化
vm.items[0] = 10 // 不会触发更新
// 无法监听数组长度变化
vm.items.length = 0 // 不会触发更新Vue 3:
javascript
// Vue 3 响应式
import { reactive } from 'vue'
const state = reactive({
user: {
name: 'John',
age: 30
},
items: [1, 2, 3]
})
// 可以监听新增属性
state.user.email = 'john@example.com' // 会触发更新
// 可以监听数组索引变化
state.items[0] = 10 // 会触发更新
// 可以监听数组长度变化
state.items.length = 0 // 会触发更新11.2 组件定义对比
Vue 2:
javascript
// Vue 2 组件
export default {
props: {
title: String
},
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
}
},
computed: {
doubleCount() {
return this.count * 2
}
},
watch: {
count(newVal) {
console.log('Count changed:', newVal)
}
},
mounted() {
console.log('Component mounted')
}
}Vue 3:
javascript
// Vue 3 组件
import { ref, computed, watch, onMounted } from 'vue';
export default {
props: {
title: String
},
setup(props) {
const count = ref(0);
const increment = () => {
count.value++;
};
const doubleCount = computed(() => count.value * 2);
watch(count, (newVal) => {
console.log('Count changed:', newVal);
});
onMounted(() => {
console.log('Component mounted');
});
return {
count,
increment,
doubleCount
};
}
};
// 或使用 <script setup>
<script setup>
import { ref, computed, watch, onMounted } from 'vue';
const props = defineProps({
title: String
});
const count = ref(0);
const increment = () => {
count.value++;
};
const doubleCount = computed(() => count.value * 2);
watch(count, (newVal) => {
console.log('Count changed:', newVal);
});
onMounted(() => {
console.log('Component mounted');
});
</script>12. 性能对比
12.1 渲染性能
| 场景 | Vue 2 | Vue 3 | 提升 |
|---|---|---|---|
| 首次渲染 | 100ms | 50ms | 50% |
| 更新渲染 | 80ms | 30ms | 62.5% |
| 内存使用 | 100MB | 50MB | 50% |
12.2 打包体积
| 场景 | Vue 2 | Vue 3 | 减少 |
|---|---|---|---|
| 基础库 | 20KB | 10KB | 50% |
| 完整库 | 30KB | 20KB | 33.3% |
13. 适用场景
13.1 Vue 2 适用场景
- 现有项目,不需要立即升级
- 对浏览器兼容性要求较高(需要支持 IE11)
- 团队对 Vue 2 更熟悉
13.2 Vue 3 适用场景
- 新项目,特别是大型项目
- 需要更好的 TypeScript 支持
- 需要更好的性能
- 需要使用组合式 API 组织代码
- 不需要支持 IE11
14. 总结
Vue 3 是 Vue 2 的重大升级,带来了许多改进和新特性:
- 响应式系统:使用 Proxy 替代 Object.defineProperty,解决了 Vue 2 响应式系统的局限性
- 虚拟 DOM:重构了虚拟 DOM,引入了 Fragment、Teleport、Suspense 等新特性
- Diff 算法:优化了 Diff 算法,引入了静态提升、补丁标志等技术
- 编译优化:引入了更先进的编译优化技术,提高了运行时性能
- 组合式 API:引入了组合式 API,提供了更好的代码组织方式
- TypeScript 支持:内置了更好的 TypeScript 支持
- 性能优化:全面优化了性能,包括渲染性能、内存使用、打包体积等
Vue 3 的设计理念更加现代化,提供了更好的开发体验和性能表现。对于新的项目,Vue 3 是一个更好的选择。对于现有的 Vue 2 项目,可以根据实际情况考虑渐进式迁移到 Vue 3。
总之,Vue 3 是 Vue 框架的一个重要里程碑,它为前端开发带来了更高效、更灵活、更现代化的解决方案。