从 Vue2 到 Vue3:一个老Vue开发者的升级指南
最近和前端同事交流发现在 vue3 的项目中还是惯性的使用 vue2 的选项式写法。这篇文章将从实践角度分享我的学习心得,希望能帮助同样准备升级的你。
为什么要升级到 Vue3?
在开始之前,我们先聊聊为什么要升级到 Vue3:
- 更好的性能:Vue3 的虚拟 DOM 重写和 Tree-shaking 支持带来了更好的性能
- 组合式 API:解决了 Vue2 中组件逻辑复用的痛点
- 更好的 TypeScript 支持:Vue3 是用 TypeScript 重写的,提供了更好的类型推导
- 更小的包体积:得益于 Tree-shaking,你可以只打包用到的功能
快速上手指南
1. 创建应用的新方式
如果你习惯了 Vue2 的写法:
// Vue2 的写法
import Vue from 'vue'
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
在 Vue3 中变成了:
// Vue3 的写法
import { createApp } from 'vue'
const app = createApp(App)
app.use(router)
app.use(store)
app.mount('#app')
个人感受:这种改变实际上更符合直觉,创建应用然后挂载插件的方式更加线性和清晰。
2. 响应式系统的变化
在 Vue2 中,我们习惯了直接在 data 中定义数据:
// Vue2
export default {
data() {
return {
count: 0,
user: {
name: '张三',
age: 25
}
}
}
}
Vue3 中需要使用 ref 或 reactive:
// Vue3
{{ count }}
{{ user.name }}
个人最佳实践:
- 优先使用 ref,因为它可以包装任何值
- 只在确实需要对象响应式的场景使用 reactive
- 记住在 JS 中需要用 .value 访问 ref 的值
3. 组合式 API 介绍
在 Vue3 中,我们可以用 Composition API 重写:
// Vue3 的 组合式 API
为什么我推荐 Composition API:
- 相关逻辑可以组织在一起,不再需要在 data、methods、computed 之间来回跳转
- 更好的代码复用能力,可以轻松提取和复用逻辑
- 更好的类型推导支持
4. 模板语法的变化
Vue3 在模板语法方面保持了大部分与 Vue2 的兼容性,但有一些值得注意的改进:
{{ item.name }}
{{ item.name }}
主要改进:
- 支持多根节点模板
- v-for 和 v-if 优先级更明确
- 更好的 TypeScript 支持
- 更好的性能优化
5. 生命周期的变化
Vue2 的生命周期钩子都有对应的 Vue3 版本,只是需要手动导入:
// Vue3
import {
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted
} from 'vue'
// 使用方式
onMounted(() => {
console.log('组件已挂载')
})
注意:
- beforeCreate 和 created 钩子在 setup 中是不需要的,因为 setup 本身就是在这两个钩子之间执行
- destroyed 改名为 unmounted
- beforeDestroy 改名为 beforeUnmount
6. 计算属性的使用
计算属性在 Vue3 中的写法更加简洁:
// Vue2
export default {
data() {
return {
firstName: '张',
lastName: '三'
}
},
computed: {
// 只读计算属性
fullName() {
return this.firstName + this.lastName
},
// 可写计算属性
fullNameWithSetter: {
get() {
return this.firstName + this.lastName
},
set(newValue) {
[this.firstName, this.lastName] = newValue.split(' ')
}
}
}
}
// Vue3
7. 监听器的改进
Vue3 的 watch 和 watchEffect 提供了更强大的监听能力:
// Vue2
export default {
data() {
return {
name: '',
userInfo: {
age: 25
}
}
},
watch: {
// 监听简单属性
name(newVal, oldVal) {
console.log('name changed:', newVal, oldVal)
},
// 监听对象属性
'userInfo.age': {
handler(newVal, oldVal) {
console.log('age changed:', newVal, oldVal)
},
deep: true,
immediate: true
}
}
}
// Vue3
8. Props 和 Emits 的声明
Vue3 提供了更明确的 Props 和 Emits 声明方式:
// Vue2
export default {
props: {
title: {
type: String,
required: true,
default: ''
}
},
methods: {
handleClick() {
this.$emit('update', { value: 'newValue' })
}
}
}
// Vue3
Props 和 Emits 的主要改进:
- 更好的类型推导
- 运行时验证
- 更清晰的事件类型定义
- 可以使用 TypeScript 的类型标注
9. 插槽(Slots)的使用变化
// Vue2
// Vue3
默认内容
头部内容
{{ data }}
10. Teleport 组件
Vue3 新增了 Teleport 组件,可以将内容渲染到 DOM 的其他位置:
11. 异步组件的改进
// Vue2
const AsyncComp = () => ({
component: import('./AsyncComp.vue'),
loading: LoadingComponent,
error: ErrorComponent,
delay: 200,
timeout: 3000
})
// Vue3
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent({
loader: () => import('./AsyncComp.vue'),
loadingComponent: LoadingComponent,
errorComponent: ErrorComponent,
delay: 200,
timeout: 3000
})
// 简写方式
const AsyncComp = defineAsyncComponent(() => import('./AsyncComp.vue'))
12. 全局 API 的改变
// Vue2
import Vue from 'vue'
Vue.config.ignoredElements = [/^app-/]
Vue.use(/* ... */)
Vue.mixin(/* ... */)
Vue.component(/* ... */)
Vue.directive(/* ... */)
// Vue3
import { createApp } from 'vue'
const app = createApp(App)
app.config.compilerOptions.isCustomElement = tag => tag.startsWith('app-')
app.use(/* ... */)
app.mixin(/* ... */)
app.component(/* ... */)
app.directive(/* ... */)
13. 自定义指令的变化
// Vue2
Vue.directive('highlight', {
bind(el, binding) {},
inserted(el, binding) {},
update(el, binding) {},
componentUpdated(el, binding) {},
unbind(el, binding) {}
})
// Vue3
app.directive('highlight', {
beforeMount(el, binding) {}, // 替代 bind
mounted(el, binding) {}, // 替代 inserted
beforeUpdate(el, binding) {}, // 新增
updated(el, binding) {}, // 替代 update + componentUpdated
beforeUnmount(el, binding) {}, // 新增
unmounted(el, binding) {} // 替代 unbind
})
14. 过渡动画的变化
hello
hello
主要变化:
- 过渡类名改变:v-enter 改为 v-enter-from
- 组件名称大写:transition 改为 Transition
- 新增 persisted 钩子用于处理缓存组件的过渡
性能优化的改进
Vue3 在性能方面有显著提升:
- 更好的 Tree-shaking 支持
- 按需引入 API
- 更小的打包体积
- Fragment 支持
- 不再需要根节点包裹
- 减少 DOM 层级
- 静态提升
- 静态节点被提升到渲染函数之外
- 减少重复创建开销
- Proxy 响应式系统
- 更好的性能
- 更完善的响应式支持
升级建议
- 渐进式迁移
- 新功能用 Vue3 + Composition API
- 老项目可以继续使用 Options API,Vue3 完全兼容
- 随着对 Composition API 的熟悉,逐步重构老代码
- 使用
- 这是 Vue3 的推荐写法
- 减少了模板引用的复杂度
- 提供了更好的开发体验
- TypeScript 支持
- 即使不用 TypeScript,也建议看看类型定义
- 配合 VS Code,可以获得很好的类型提示
- 响应式系统使用建议
- 优先使用 ref
- 必要时使用 reactive
- 注意 ref 的 .value 使用
结语
作为一个经历过从 Vue2 到 Vue3 迁移的开发者,我认为这次升级绝对值得。虽然学习曲线有点陡,但 Composition API 带来的好处远超过学习成本。建议大家在新项目中直接使用 Vue3,享受新版本带来的各种改进。
记住:Vue3 不是一个完全不同的框架,它是 Vue2 的自然进化,保持耐心,你会发现新的开发方式更加优雅和高效。