广告:Codex Token 低价中转站稳定接口 · 快速接入 · 开发者备用通道
Engineering article

Vue 3组合式API迁移 | 实战干货 构建优化

Vue 3组合式API迁移是真的能提升项目维护效率的,但别指望它能自动帮你改代码。我见过太多人用Vue 2的选项式API写到一半突然想改成组合式API,结果一顿操作猛如虎,最后代码还更乱了。迁移到组合式API,最核心的是用setup函数代替data、methods、computed这些老方法,然后引入ref和reactive管理响应式数据

Vue 3组合式API迁移 | 实战干货 构建优化
配图来源于网络和AI生成,仅供参考。
▌ 技术引导
Vue 3组合式API迁移是真的能提升项目维护效率的,但别指望它能自动帮你改代码。我见过太多人用Vue 2的选项式API写到一半突然想改成组合式API,结果一顿操作猛如虎,最后代码还更乱了。迁移到组合式API,最核心的是用setup函数代替data、methods、computed这些老方法,然后引入ref和reactive管理响应式数据。你得记住,setup函数返回的值,是所有组件内部可访问的,不能光想着用script setup,那只是组合式API的糖衣。迁移过程中,组件内部的生命周期钩子比如onMounted、onUnmounted必须明确引入,不能漏掉。还有,别忘了用defineProps和defineEmits声明props和emits,不然编译会报错。我之前在一个项目里,迁移后因为没正确配置emits导致父子组件通信出问题,浪费了整整三天调试时间。

▌ 技术参考


Vue 3组合式API迁移的关键在于理解setup函数和响应式数据的使用方式。在Vue 2中,组件通过data返回对象定义响应式状态,而在Vue 3中,你需要用ref和reactive来分别创建基本类型和对象类型的响应式变量。比如,声明一个响应式变量count,可以直接使用const count = ref(0)。如果数据结构复杂,比如一个对象内部还有嵌套对象或数组,应该用reactive包裹。在迁移时,必须将原来data中的所有变量都转换为ref或reactive,否则它们不会被正确识别为响应式数据。如果你在迁移过程中遇到组件状态没有更新的问题,十有八九是因为漏掉了响应式声明或者在setup函数外面直接修改了变量值。


迁移过程中,你需要将原来的methods转换为setup函数内部的函数,或者使用defineExpose来暴露方法。比如,原来在Vue 2中写了一个方法updateData(),现在应该在setup函数中定义它,然后通过defineExpose暴露出来。这在项目后期如果还要复用某些方法,会更容易管理。同时,在Vue 3中,组件内部的生命周期钩子不能再用beforeMount、mounted这样的命名方式,而是变成了onBeforeMount、onMounted。如果你在迁移时没有替换成这些新钩子,组件的初始化逻辑就会失效。记得在setup函数中,使用onMounted或onBeforeMount来执行初始化操作,而不是在模板中触发。


props和emits的迁移需要特别注意。Vue 2中props是通过props选项声明的,而Vue 3中需要用defineProps函数。如果项目里用的是TypeScript,定义props时还要加上类型声明。比如,defineProps<{ message: string }>()。同时,emits也需要用defineEmits来声明,否则组件的事件不会被正确识别。在迁移到组合式API时,不要把emit写成this.$emit,应该是通过defineEmits返回的函数触发。如果你在迁移后发现事件没被正确触发,检查一下是否漏掉了defineEmits的声明,或者是否在setup函数内部错误地使用了事件触发方式。


使用组合式API时,组件内部的状态管理会变得更灵活,但同时也可能引入一些性能瓶颈。比如,如果你在组件中频繁地创建ref或reactive对象,没注意复用策略,可能会影响渲染性能。在Vue 3中,你可以使用toRefs将reactive对象转换为普通对象,这样在模板中访问时就不会触发响应式转换,减少不必要的计算。另外,如果你发现组件在大型数据集上渲染特别卡,可以考虑用v-for配合key来优化列表渲染,或者用虚拟滚动库来减少DOM节点数量。在迁移到组合式API后,原本的组件结构必须重新梳理,才能真正发挥性能优势。


大多数项目在迁移组合式API时都会遇到一个老大难的问题:组件内部的逻辑混杂。尤其是在Vue 2中写习惯了选项式API,迁移到组合式API后,如果不按模块化方式组织代码,setup函数里会堆满各种逻辑,变得难以维护。解决办法是把每个功能模块封装成独立的函数,比如用useCounter、useFetch等来管理不同的状态。这样不仅让代码结构更清晰,还能方便复用。我之前接手一个Vue 2项目迁移到Vue 3,结果组件setup函数里塞了几十行代码,后来硬着头皮把逻辑拆分为多个自定义Hook,最后代码量反而少了,维护起来也轻松了。别怕写多个自定义Hook,这在组合式API中是标配。


Vue 3的组合式API引入了新的响应式系统,但它的底层逻辑和Vue 2相比变化并不大。你还是可以用Vue 3的响应式系统来管理组件状态,只是方式变了。比如,原来用this.$set修改数组元素,现在应该用Vue.set或者直接使用数组的push、splice方法。在Vue 3中,你还可以用watch和watchEffect来监听响应式变量的变化,但要明确它们的区别。watch适合监听单一变量,而watchEffect适合监听多个变量,甚至可以自动追踪依赖。在迁移过程中,如果你发现组件的watch监听失效,检查一下是否在setup函数外部声明了watch或者是否在setup函数内部错误地使用了响应式变量。


迁移到组合式API后,组件的结构也随之发生了变化。原来在Vue 2中,组件的逻辑和模板是紧密结合的,而在Vue 3中,你需要通过setup函数来返回组件内部可用的变量和方法,包括props、emits、ref、reactive等。这要求你在编写组件时更加注重模块化和可读性。比如,你可以把组件的逻辑拆分成多个函数,再通过setup函数整合。在Vue 3中,setup函数是组件的入口点,所有逻辑都应该在这个函数中展开。如果你在迁移过程中发现setup函数过于臃肿,这时候就需要抽出一些逻辑到自定义Hook中,保持组件的简洁性。


组合式API的一个好处是它能很好地支持TypeScript,但在实际操作中,你会发现TypeScript的类型声明也需要重新调整。比如,原来在Vue 2中用props: { message: String }来声明变量类型,现在需要改成defineProps<{ message: string }>()。同样,emits也需要用defineEmits来声明,否则TypeScript会报错。如果你在迁移到组合式API后遇到TypeScript编译错误,可能是因为没有正确声明props和emits的类型。这时候要确保每个props和emits都有对应的类型定义,甚至可以使用TS的泛型来提高代码的灵活性。迁移到组合式API后,TypeScript会成为你的新武器,帮你提前发现程序中的潜在错误。


在迁移过程中,你可能会遇到很多Vue 2的语法习惯带来的问题。比如,Vue 2中很多地方用了this来访问组件实例,而Vue 3中,setup函数中的this是无效的,所有代码都必须写在setup函数内部。这会让一些老项目在迁移时出现错误,比如this.$data、this.$props这些写法都需要替换。如果你在迁移后发现代码在setup函数里运行不了,那可能是你误用了this。这时候要记住,setup函数内部的this是undefined,所有数据和方法都必须通过ref、reactive或者变量来访问。这需要你重新审视代码结构,确保所有逻辑都正确地放在setup函数内部。


Vue 3的组合式API不仅支持setup函数,还引入了script setup语法,这在Vue 3.2之后变得越来越流行。script setup的写法更接近JavaScript的原生风格,不需要显式调用setup函数或者返回值。比如,在script setup中可以直接写ref和reactive,然后在模板中使用。但要注意,script setup是Vue 3的特性,不兼容Vue 2,所以如果你的项目同时需要兼容Vue 2和Vue 3,就只能用setup函数。另外,script setup中的变量和方法不会自动暴露到模板中,所以必须用expose来让父组件访问到。如果你在script setup中写了一个方法,但父组件调用失败,那可能是你漏掉了expose声明。

十一
在处理复杂的组件逻辑时,组合式API的优势会非常明显。比如,如果你有一个组件需要管理多个状态,或者需要处理多个异步请求,用setup函数和自定义Hook的方式会比Vue 2的选项式API更清晰。你可以用useFetch来封装数据获取逻辑,或者用useState来统一管理组件的响应式状态。这种模块化的方式能让代码结构更整洁,也更容易维护。我之前在迁移一个数据表组件时,把数据获取、筛选、分页等逻辑都封装成了自定义Hook,最后setup函数只负责接收props和暴露方法,代码量反而更小了。这说明,组合式API的核心不是语法改变,而是思维方式的转变。

十二
Vue 3的组合式API还引入了响应式函数,比如computed和watch,这些函数在组合式API中和选项式API的写法完全不同。比如,原来用computed: () => {}来声明计算属性,现在需要改成computed(() => {})。在Vue 3中,watch函数的参数和返回值也要调整,它不再接受Vue 2的回调函数,而是用一个函数来定义监听逻辑。如果你在迁移过程中发现watch失效,那可能是你没有正确传递参数,或者没有使用watchEffect来监听多个变量。这时候要记住,watch适用于单一变量,watchEffect适用于多个变量,它们的用法和生命周期管理都有所不同。

十三
在迁移到组合式API时,你可能会发现组件之间的通信变得更复杂。比如,父组件通过props传递数据,子组件通过emits触发事件,这种模式在组合式API中依然适用,但需要重新组织代码结构。你可以用provide和inject来实现跨层级的通信,或者用Vuex和Pinia这样的状态管理库。在Vue 3中,Vuex也支持组合式API,但推荐使用Pinia,因为它的API更简洁,而且不需要像Vuex那样写大量的mutations和actions。如果你在迁移过程中发现组件通信效率低下,可以考虑用Pinia来统一管理状态,避免重复的props和emits传递。这在大型项目中尤其重要。

十四
Vue 3的组合式API在升级过程中,有些细节需要特别注意。比如,在setup函数中,如果你定义了一个ref变量,却在组件外使用了它,可能会导致内存泄漏或者状态失效。这时候要用defineExpose来暴露变量,或者在组件销毁时手动解除引用。还有,如果你在setup函数中使用了useRouter、useStore这些Vue 3的组合式API工具,它们会自动注入到组件中,不需要额外配置。但要确保你的项目已经正确配置了Vue Router和Pinia,否则这些工具可能会失效。在迁移过程中,检查这些依赖是否正确引入是关键一步。

十五
现在来说,组合式API真的能让你的代码更简洁,但也可能让你的项目结构更复杂。如果你在迁移过程中发现组件之间混乱不堪,可能是因为你没有合理规划模块化结构。这时候,可以借助Vue 3的模块化特性,把每个功能模块拆分成独立的文件,再用import方式引入。比如,把一个表单验证的逻辑写成一个单独的useFormValidation文件,再在setup函数中导入使用。这种做法不仅让代码结构更清晰,还方便团队协作和后续维护。不过,模块化也需要一定成本,特别是对于小型项目,可能反而增加了复杂度。所以,要根据项目规模来决定是否采用模块化方式。