Vue3 的设计目标是什么?相比 Vue2 做了哪些关键优化?
刚开始用 Vue2 的时候,感觉就像拿到了一把顺手的新工具。简单、直观,做东西也特别快。
但随着项目越来越复杂,项目渐渐的也会遇到一些烦恼。组件的代码越写越长,相关的逻辑分散在data、methods、computed各种不同的地方,想复用一段功能也挺费劲的。同时打包后的文件也越来越大。
后来用了Vue3,可以说是完美的解决了我上面的几个痛点。
下面我们就来看下 vue3 到底做了哪些优化。
Vue 3.0 的四大设计目标
任何一次技术重构都有其核心目标,Vue 3 主要围绕以下四点展开:
- 更小:通过 Tree-shaking 等技术,让打包体积比 Vue 2 更小。
- 更快:优化虚拟 DOM,提升渲染和更新性能。
- 更易维护:采用 TypeScript 重写,代码结构更清晰模块化。
- 更好的扩展性:提供组合式 API 等新特性,应对复杂应用场景。
下面我们就来看看这些目标是如何具体实现的。
性能优化:底层引擎的重构
1. 响应式系统:Proxy 替代 Object.defineProperty
这是 Vue3 最核心的改进之一。
Vue2的响应式原理:
// Vue2 使用 Object.defineProperty
const data = {};
Object.defineProperty(data, 'name', {
get() {
console.log('读取name');
return '张三';
},
set(newVal) {
console.log('设置name:', newVal);
}
});
Vue2 的局限性:
- 无法检测属性的添加和删除
- 对数组的支持需要特殊处理
- 初始化时需要递归遍历整个对象
Vue3 的解决方案:
// Vue 3 使用 Proxy
const data = { name: '张三' };
const proxyData = new Proxy(data, {
get(target, key) {
console.log(`读取${key}`);
return target[key];
},
set(target, key, value) {
console.log(`设置${key}:`, value);
target[key] = value;
return true;
},
deleteProperty(target, key) {
console.log(`删除${key}`);
delete target[key];
return true;
}
});
Proxy 的优势:
- 可以监听动态添加的属性
- 支持数组索引修改、length 修改
- 支持 Map、Set 等数据结构
- 性能更好
2. 编译时优化:模板编译
Vue3 的编译器会分析模板,为动态内容添加标记。
// 模板
<template>
<div class="header">
<img src="./logo.png" />
<h1>{{ title }}</h1>
</div>
</template>
Vue2:每次渲染都重新创建 <img> 节点(虽然是静态的)。
Vue3:编译时发现 <img> 永远不变,就把它缓存起来,只创建一次!
// 编译后伪代码(简化)
const staticImg = createVNode('img', { src: './logo.png' })
function render() {
return createVNode('div', { class: 'header' }, [
staticImg, // 直接复用!
createTextVNode(ctx.title) // 只有这里动态更新
])
}
3. Tree-shaking:按需引入
Vue3 的模块化设计使得未使用的功能不会被打包:
import { createApp, h } from 'vue'; // 只引入需要的API
如果你不使用 transition 组件,它的代码就不会出现在最终打包文件中
开发体验的升级:组合式 API
这是 Vue3 在代码组织方式上的重大改进!
Vue2 Options API 的问题:
export default {
data() {
return {
users: [],
searchQuery: '',
loading: false
}
},
methods: {
fetchUsers() {
// 获取用户数据
},
searchUsers() {
// 搜索用户
}
},
computed: {
filteredUsers() {
// 过滤用户
}
},
mounted() {
this.fetchUsers();
}
}
同一个功能(用户管理)的逻辑被拆分到不同的选项中,组件复杂后难以维护。
Vue3 Composition API 的解决方案:
<template>
<div>
<input v-model="searchQuery" placeholder="搜索用户">
<div v-if="loading">加载中...</div>
<UserList :users="filteredUsers" />
</div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
// 用户管理功能 - 所有相关逻辑在一起
const users = ref([])
const searchQuery = ref('')
const loading = ref(false)
const filteredUsers = computed(() => {
return users.value.filter(user =>
user.name.includes(searchQuery.value)
)
})
async function fetchUsers() {
loading.value = true
try {
users.value = await api.getUsers()
} finally {
loading.value = false
}
}
onMounted(() => {
fetchUsers()
})
// 其他功能也可以这样组织...
// const posts = ref([])
// const fetchPosts = async () => { ... }
</script>
vue3 组合式函数:
// composables/useUserManagement.js
import { ref, computed, onMounted } from 'vue'
export function useUserManagement() {
const users = ref([])
const searchQuery = ref('')
const loading = ref(false)
const filteredUsers = computed(() => {
return users.value.filter(user =>
user.name.includes(searchQuery.value)
)
})
async function fetchUsers() {
loading.value = true
try {
users.value = await api.getUsers()
} finally {
loading.value = false
}
}
onMounted(fetchUsers)
return {
users,
searchQuery,
loading,
filteredUsers,
fetchUsers
}
}
<script setup>
// 在组件中使用
import { useUserManagement } from './composables/useUserManagement'
const {
users,
searchQuery,
loading,
filteredUsers,
fetchUsers
} = useUserManagement()
</script>
Vue3 同时支持 Options API 和 Composition API,你可以根据项目复杂度和个人偏好选择,甚至混合使用。
其他重要新特性
1. Teleport:任意传送
<template>
<div class="app">
<button @click="showModal = true">打开弹窗</button>
<!-- 将弹窗内容传送到 body 下 -->
<Teleport to="body">
<div v-if="showModal" class="modal">
<h2>我是弹窗</h2>
<button @click="showModal = false">关闭</button>
</div>
</Teleport>
</div>
</template>
2. Fragments:多根节点支持
<template>
<!-- Vue 3 支持多个根节点 -->
<header>头部</header>
<main>主要内容</main>
<footer>底部</footer>
</template>
3. Suspense:异步组件处理
<template>
<Suspense>
<template #default>
<AsyncComponent />
</template>
<template #fallback>
<div>加载中...</div>
</template>
</Suspense>
</template>
<script setup>
import { defineAsyncComponent } from 'vue'
// 异步组件
const AsyncComponent = defineAsyncComponent(() =>
import('./AsyncComponent.vue')
)
</script>
总结
| 特性对比 | Vue 2 | Vue 3 | 优势 |
|---|---|---|---|
| 响应式系统 | Object.defineProperty | Proxy | 功能更完善,性能更好 |
| 代码组织 | Options API | Composition API | 逻辑复用和组织更灵活 |
| TypeScript | 需要额外配置 | 原生支持 | 开发体验更好 |
| 包大小 | 全量引入 | Tree-shaking | 打包体积更小 |
| 新功能 | 有限 | Teleport、Suspense等 | 开发能力更强 |
当然 Vue3 并没有抛弃过去,而是把选择权交给了我们:小项目可以用熟悉的 Options API 快速上手,大项目则可以借助 Composition API 和 TypeScript 更从容的组织代码。
本文首发于公众号:程序员刘大华,专注分享前后端开发的实战笔记。关注我,少走弯路,一起进步!
📌往期精彩
《重构了20个SpringBoot项目后,总结出这套稳定高效的架构设计》
《代码里全是 new 对象,真的很 Low 吗?我认真想了一晚》