Vue 2 到 Vue 3 的语法演进:从 Options API 到 Composition API 完全指南

发布时间:2026/7/11 1:18:02

Vue 2 到 Vue 3 的语法演进:从 Options API 到 Composition API 完全指南 前言在前几篇博客中我们从 Vue 项目的脚手架选型聊到 VSCode 配置再到 Vue 的核心指令。今天我们终于要触及 Vue 3 最核心、最颠覆性的变化——Composition API组合式 API。如果你是一位 Vue 2 的老用户初次接触 Composition API 时可能会有一种“这不像 Vue”的陌生感。如果你是一位刚入门的新手面对 Options API 和 Composition API 两种写法可能会陷入“我到底该学哪个”的困惑。本文将带你系统理解从 Options API 到 Composition API 的演进逻辑通过大量代码对比和真实项目案例帮你建立起清晰的认知——不仅知道怎么写更知道为什么这么写。一、为什么要从 Options API 走向 Composition API1.1 Options API 是什么Options API 是 Vue 2 及 Vue 3 中依然支持的传统写法。它将组件的逻辑分散到不同的“选项”中——data、methods、computed、watch、mounted等。// Options API 写法 —— Vue 2 和 Vue 3 都支持exportdefault{data(){return{count:0,user:{name:,age:0}}},computed:{doubledCount(){returnthis.count*2}},methods:{increment(){this.count}},mounted(){console.log(组件已挂载)}}这种写法对初学者非常友好结构清晰一看就懂。在小型项目中Options API 依然是一个不错的选择。1.2 Options API 的痛点然而当项目变得复杂时Options API 的局限性就会暴露出来。痛点一逻辑分散难以维护假设我们要实现一个用户信息表单包含加载用户数据、表单验证、提交保存、加载状态管理等。在 Options API 中这些逻辑会被强制分散到不同的选项中exportdefault{data(){return{// 用户数据相关user:{name:,email:},// 加载状态相关loading:false,// 验证错误相关errors:{}}},methods:{// 加载用户数据的方法fetchUser(){/* ... */},// 表单验证的方法validateForm(){/* ... */},// 提交保存的方法submitForm(){/* ... */}},computed:{// 与用户数据相关的计算isFormValid(){/* ... */}},mounted(){// 加载用户数据this.fetchUser()}}同一个功能比如“加载用户数据”的代码散落在data、methods、mounted三个不同的地方。当组件有十几个功能时你需要在不同选项之间来回跳转代码的可读性和可维护性急剧下降。痛点二逻辑复用困难在 Options API 中逻辑复用主要靠Mixins混入。但 Mixins 存在几个致命问题命名冲突两个 Mixin 定义了同名方法后者会覆盖前者来源不清晰组件中使用的属性和方法不知道来自哪个 Mixin隐式依赖Mixin 之间可能相互依赖难以追踪1.3 Composition API 的解决方案Composition API 的核心思想是按功能组织代码而不是按类型分类。如果说 Options API 是把所有“写周报”需要的东西放一起、所有“画设计稿”需要的东西放一起那么 Composition API 就是做什么事拿什么包干净利落。Composition API 通过setup()函数将相关的状态、方法、计算属性、生命周期钩子放在一起使得代码组织更加灵活和清晰。二、核心语法对比从 Options API 到 Composition API2.1 响应式数据定义Options APIVue 2 Vue 3exportdefault{data(){return{count:0,user:{name:张三,age:25}}}}Composition APIVue 3import{ref,reactive}fromvueexportdefault{setup(){// 基本类型用 refconstcountref(0)// count 是一个 Ref 对象通过 .value 访问// 对象类型用 reactiveconstuserreactive({name:张三,age:25})return{count,user}}}refvsreactive的使用建议场景推荐 API原因基本类型number、string、booleanrefreactive不支持基本类型对象/数组需要整体替换refreactive重新赋值会丢失响应式复杂对象表单、配置项reactive无需.value访问更自然不确定用哪个ref更通用适合任意类型2.2 计算属性Options APIexportdefault{data(){return{message:Hello, Vue!}},computed:{reversedMessage(){returnthis.message.split().reverse().join()}}}Composition APIimport{ref,computed}fromvueexportdefault{setup(){constmessageref(Hello, Vue!)constreversedMessagecomputed((){returnmessage.value.split().reverse().join()})return{message,reversedMessage}}}2.3 方法Options APIexportdefault{data(){return{count:0}},methods:{increment(){this.count}}}Composition APIimport{ref}fromvueexportdefault{setup(){constcountref(0)constincrement(){count.value}return{count,increment}}}注意在 Composition API 中方法就是普通的 JavaScript 函数不再需要通过methods选项包裹。2.4 生命周期钩子Vue 3 将生命周期钩子转换为可导入的函数形式Vue 2 Options APIVue 3 Composition APIbeforeCreate不需要setup()之前执行created不需要setup()之前执行beforeMountonBeforeMountmountedonMountedbeforeUpdateonBeforeUpdateupdatedonUpdatedbeforeDestroyonBeforeUnmountdestroyedonUnmountedOptions APIexportdefault{data(){return{count:0}},mounted(){console.log(组件已挂载count ,this.count)},beforeDestroy(){console.log(组件即将销毁)}}Composition APIimport{ref,onMounted,onBeforeUnmount}fromvueexportdefault{setup(){constcountref(0)onMounted((){console.log(组件已挂载count ,count.value)})onBeforeUnmount((){console.log(组件即将销毁)})return{count}}}2.5 侦听器watchOptions APIexportdefault{data(){return{searchText:}},watch:{searchText(newVal,oldVal){console.log(搜索词变化,oldVal,-,newVal)}}}Composition APIimport{ref,watch}fromvueexportdefault{setup(){constsearchTextref()watch(searchText,(newVal,oldVal){console.log(搜索词变化,oldVal,-,newVal)})return{searchText}}}三、script setup语法糖更简洁的 Composition API在 Vue 3.2 中官方引入了script setup语法糖让 Composition API 的写法更加简洁。使用setup()函数的写法script import { ref, onMounted } from vue export default { setup() { const count ref(0) const increment () count.value onMounted(() { console.log(mounted) }) return { count, increment } // 必须手动 return } } /script使用script setup语法糖的写法script setup import { ref, onMounted } from vue // 直接定义无需 return const count ref(0) const increment () count.value onMounted(() { console.log(mounted) }) /scriptscript setup的优势代码更少不需要export default和return更自然的书写体验顶层的变量和函数自动暴露给模板更好的性能编译时优化避免了不必要的代理层和this上下文处理更好的 TypeScript 支持类型推断更加精准官方推荐如果你使用单文件组件SFC和 Composition APIscript setup是官方推荐的语法。四、真实项目实战从 Options API 到 Composition API4.1 场景一用户信息表单中型组件假设我们要实现一个用户信息编辑表单包含加载用户数据、表单验证、提交保存、加载状态管理。Options API 实现代码分散 script export default { data() { return { // ---- 用户数据 ---- user: { name: , email: , phone: }, // ---- 表单状态 ---- loading: false, errors: {}, isSubmitting: false } }, computed: { // ---- 表单验证 ---- isFormValid() { return this.user.name.trim() this.user.email.trim() this.user.phone.trim() } }, methods: { // ---- 加载数据 ---- async fetchUser() { this.loading true try { const res await api.getUser(this.$route.params.id) this.user res.data } finally { this.loading false } }, // ---- 表单验证 ---- validateField(field) { if (!this.user[field]?.trim()) { this.errors[field] 此字段为必填项 } else { delete this.errors[field] } }, // ---- 提交保存 ---- async submitForm() { this.isSubmitting true try { await api.updateUser(this.user) this.$message.success(保存成功) } finally { this.isSubmitting false } } }, mounted() { this.fetchUser() } } /scriptComposition API 实现按功能分组 script setup import { ref, reactive, computed, onMounted } from vue import { useRoute } from vue-router import { ElMessage } from element-plus import api from /api const route useRoute() // 1. 用户数据管理 const user reactive({ name: , email: , phone: }) const loading ref(false) const fetchUser async () { loading.value true try { const res await api.getUser(route.params.id) Object.assign(user, res.data) // 保持响应式 } finally { loading.value false } } // 2. 表单验证 const errors reactive({}) const isFormValid computed(() { return user.name.trim() user.email.trim() user.phone.trim() }) const validateField (field) { if (!user[field]?.trim()) { errors[field] 此字段为必填项 } else { delete errors[field] } } // 3. 提交保存 const isSubmitting ref(false) const submitForm async () { isSubmitting.value true try { await api.updateUser(user) ElMessage.success(保存成功) } finally { isSubmitting.value false } } // 4. 初始化加载 onMounted(() { fetchUser() }) /script对比总结Composition API 版本将代码按功能模块用户数据管理、表单验证、提交保存清晰分组每个功能的相关代码集中在一起一目了然。4.2 场景二逻辑复用 —— 封装组合式函数这是 Composition API 最强大的能力之一——将可复用的逻辑封装成组合式函数Composables。场景多个页面都需要实现列表数据的分页加载功能。封装usePagination组合式函数// composables/usePagination.jsimport{ref,reactive,computed}fromvueexportfunctionusePagination(fetchFn){// 状态constlistref([])constloadingref(false)constpaginationreactive({page:1,pageSize:20,total:0})// 计算属性consthasMorecomputed((){returnpagination.totalpagination.page*pagination.pageSize})// 方法constloadDataasync(resetfalse){if(reset){pagination.page1list.value[]}loading.valuetruetry{constresawaitfetchFn({page:pagination.page,pageSize:pagination.pageSize})list.valuereset?res.items:[...list.value,...res.items]pagination.totalres.total}finally{loading.valuefalse}}constloadMore(){if(hasMore.value!loading.value){pagination.pageloadData()}}// 返回给组件使用的 APIreturn{list,loading,pagination,hasMore,loadData,loadMore}}在组件中使用script setup import { usePagination } from /composables/usePagination import { getOrderList } from /api/order // 只需一行代码即可获得完整的分页能力 const { list, loading, loadData, loadMore } usePagination(getOrderList) // 组件挂载时自动加载第一页 loadData(true) /script template div div v-foritem in list :keyitem.id{{ item.name }}/div button clickloadMore :disabledloading加载更多/button /div /template组合式函数的命名规范统一使用use前缀如usePagination、useTable、useForm等。对比 Mixins组合式函数彻底解决了 Mixins 的命名冲突和来源不清晰的问题。每个组合式函数返回的对象都是显式的你可以清楚地知道每个属性和方法来自哪里。五、Vue 2 到 Vue 3 的其他语法变化5.1 响应式系统从defineProperty到Proxy这是 Vue 3 最底层的变革对比维度Vue 2Vue 3实现方式Object.definePropertyProxy新增属性需要Vue.set()直接添加即可触发更新删除属性需要Vue.delete()直接delete即可触发更新数组索引修改无法检测✅ 支持性能较慢提升约 2 倍5.2 全局 API 的变化Vue 2Vue 3new Vue()createApp()Vue.component()app.component()Vue.directive()app.directive()Vue.mixin()app.mixin()Vue.use()app.use()5.3v-model的变化对比Vue 2Vue 3默认 propvaluemodelValue默认事件inputupdate:modelValue多个绑定不支持✅ 支持v-model:name、v-model:email.sync修饰符✅ 支持❌ 已移除用v-model:propName替代5.4 移除的 API$on、$off、$once实例事件方法被移除filters过滤器推荐用计算属性或方法替代$children推荐使用$refs或组件间通信.native修饰符Vue 3 自动将事件监听添加到根元素六、如何选择Options API vs Composition API6.1 选择 Options API 的场景小型项目或简单组件逻辑简单Options API 足够团队对 Vue 2 非常熟悉迁移成本考虑展示型组件主要是展示数据逻辑很轻6.2 选择 Composition API 的场景中大型项目代码组织更清晰可维护性更强复杂组件多个功能交织需要按逻辑分组需要逻辑复用多个组件共享相同逻辑使用 TypeScriptComposition API 的类型推断更精准新项目拥抱未来Vue 3 的官方推荐方向6.3 迁移策略对于已有的 Vue 2 项目不建议一次性全部重写新组件使用 Composition API在新功能或新页面中率先使用逐步迁移旧组件在有重构需求时将旧组件改为 Composition API使用vue/compat迁移构建在 Vue 3 上运行 Vue 2 代码观察兼容警告并逐项修复七、总结从 Options API 到 Composition API 的演进本质上是 Vue 从框架主导走向开发者主导的转变维度Options APIVue 2Composition APIVue 3代码组织按类型分类data、methods、computed按功能分组相关逻辑放在一起逻辑复用Mixins命名冲突、来源不清晰组合式函数显式、可追踪TypeScript 支持基础支持this类型推断困难原生支持类型推断精准学习曲线平缓对初学者友好陡峭需要理解新的编程范式适用场景小型项目、简单组件中大型项目、复杂组件Composition API 不是要取代 Options API而是为开发者提供了一个更强大、更灵活的选择。Vue 3 同时支持两种 API 风格你可以在同一个项目中混合使用。理解这段演进历程不仅能帮你写出更好的 Vue 代码更能让你在面对新技术时保持一份“知其然知其所以然”的从容。

相关新闻