Code前端首页关于Code前端联系我们

Vue3 Composition 到底好在哪?怎么用更顺手?

terry 17小时前 阅读数 122 #SEO

很多同学从 Vue2 转到 Vue3 后,对 Composition API 又爱又怕:爱它能解决旧写法的痛点,怕自己没摸清套路写得更乱,今天用问答形式把 Composition 的关键问题掰碎了讲,看完你对它的理解至少深一层~

Composition API 是为了解决 Options API 的哪些痛点?

Vue2 用 Options API(把代码拆到 datamethodscomputed 这些选项里)写组件时,中小项目还能应付,但项目一复杂就暴露问题:

  • 逻辑复用难:想把“获取用户信息 + 表单验证”这类逻辑复用,只能用 mixin,但多个 mixin 会导致命名冲突(比如不同 mixin 都定义了 handleSubmit),而且组件里突然多了一堆方法,根本分不清哪个 mixin 提供的(这叫“命名空间污染”)。
  • 代码组织乱:一个组件同时处理“用户信息、订单列表、弹窗逻辑”时,相关代码被拆到 datamethodscomputed 里,要看某块逻辑得来回翻代码,就像“把完整的拼图拆得七零八落”。
  • TS 支持弱:Options API 里的 this 指向不明确,给 TypeScript 类型推导使绊子,写类型定义特别费劲。

Composition API 就是冲着这些痛点来的:把逻辑按功能聚合,而非按选项拆分,还让逻辑复用更干净、对 TypeScript 友好度拉满。

用 Composition 组织代码,和之前有啥不一样?

核心变化是从“按选项分类”变成“按功能聚合”,举个例子:做一个“用户信息编辑 + 订单列表”的页面,用 Options API 得这么写:

export default {
  data() { return { user: {}, orders: [] } },
  methods: { 
    fetchUser() {}, 
    updateUser() {}, 
    fetchOrders() {} 
  },
  computed: { userFullName() {} }
}

代码分散在不同选项里,找“用户相关逻辑”得跳着看,换成 Composition API(用 <script setup> 语法更简洁):

<script setup>
import { ref, computed } from 'vue'
// 把“用户逻辑”聚合成一个功能块
const user = ref({})
const userFullName = computed(() => `${user.value.firstName} ${user.value.lastName}`)
function fetchUser() { /* 请求用户信息 */ }
function updateUser() { /* 提交用户修改 */ }
// 把“订单逻辑”聚合成另一个功能块
const orders = ref([])
function fetchOrders() { /* 请求订单列表 */ }
</script>

如果项目里有多个组件需要“用户逻辑”,还能把这堆代码抽到 自定义 Hook(useUser.js 里,组件里一行 import { useUser } from './useUser' 就能复用,逻辑来源清晰,再也不怕命名冲突~

ref、reactive 这些响应式 API 怎么选?

Vue3 响应式靠 refreactive 这俩“基石”,但用法有讲究:

  • ref:用来包基本类型(字符串、数字、布尔)或者对象/数组,特点是“需要 .value 访问”(模板里自动解包,不用写 .value)。const count = ref(0),修改时要 count.value++
  • reactive:用来包对象/数组,直接代理整个对象,修改时不用 .value,但有个坑:如果给 reactive 变量重新赋值(user = { name: '新名字' }),会直接切断响应式!所以更推荐用 ref 包对象(const user = ref({})),修改属性 user.value.name = '新名字' 能保留响应式。

简单总结:

  • 基本类型选 ref
  • 对象/数组优先用 ref(避免 reactive 赋值丢响应式的坑);
  • 要是想直接操作对象属性,且确定不会整对象替换,用 reactive 也没问题。

computed(计算属性)和 watch(监听)也得跟上:

  • computed 跟 Options API 里的差不多,但要手动导入,写成 const 全名 = computed(() => 逻辑)
  • watch 更灵活了,可以同时监听多个数据源,watch([count, user], ([newCount, newUser]) => { /* 数据变化后执行逻辑 */ })

自定义 Hook 为啥能让逻辑复用更爽?

自定义 Hook 是 Composition 逻辑复用的“灵魂”,本质是把一段逻辑(数据 + 方法)封装成函数,让其他组件能直接拿走去用

举个实际场景:很多组件需要“发起异步请求 + 加载状态 + 错误处理”,用自定义 Hook useFetch.js 封装:

import { ref, onMounted } from 'vue'
export function useFetch(url) {
  const data = ref(null)
  const loading = ref(true)
  const error = ref(null)
  onMounted(async () => {
    try {
      const res = await fetch(url)
      data.value = await res.json()
    } catch (e) {
      error.value = e
    } finally {
      loading.value = false
    }
  })
  return { data, loading, error }
}

其他组件想用,直接导入:

<script setup>
import { useFetch } from './useFetch'
const { data, loading, error } = useFetch('https://api.example.com/data')
</script>

对比 mixin 有三大优势:

  1. 来源清晰:组件里能清楚看到 useFetch 提供了哪些数据和方法,mixin 则是“暗箱操作”;
  2. 避免冲突:Hook 里的变量是局部的,不会和组件或其他 Hook 重名;
  3. 灵活传参:给 useFetch 传不同的 url,就能复用一套逻辑请求不同接口,mixin 做不到这么灵活。

旧项目想切 Composition,怎么平稳过渡?

不用一刀切!分三步走:

  1. 新功能用 Composition:新增组件或功能时,直接用 <script setup> + Composition API 写,旧代码保留 Options API,Vue3 完全兼容;
  2. 逐步重构旧组件:把旧组件的 datamethods 逻辑,慢慢搬到 setup 里,比如先把一个方法抽到 setup,用 ref 代替 data 里的变量,再逐步迁移其他逻辑;
  3. 复用逻辑抽成 Hook:遇到重复逻辑(比如弹窗管理、权限判断),抽成自定义 Hook,新旧组件都能复用,减少重复代码。

举个重构小例子,旧组件里的计数器:

// 旧写法(Options API)
export default {
  data() { return { count: 0 } },
  methods: { increment() { this.count++ } }
}

改成 Composition:

// 新写法(Composition API)
<script setup>
import { ref } from 'vue'
const count = ref(0)
function increment() { count.value++ }
</script>

一步步来,风险低还能积累经验~

和 Pinia 结合时,Composition 能玩出啥新花样?

Pinia 是 Vue 官方推荐的状态管理工具,和 Composition API 天生一对,以前 Vuex 写模块很繁琐,Pinia 用 Composition 思路定义 Store 更简单:

// store/user.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useUserStore = defineStore('user', () => {
  const user = ref({ name: '张三' })
  const fullName = computed(() => user.value.name + '(别名)')
  function updateName(newName) { user.value.name = newName }
  return { user, fullName, updateName }
})

这种“setup 语法”定义 Store,和组件里用 Composition API 几乎一样,学习成本低,Store 里的逻辑还能拆成自定义 Hook,比如把“用户权限判断”抽到 usePermission.js,Store 里导入复用,逻辑分层更清晰。

实际开发里,Composition 有哪些容易踩的坑?

新手最容易栽的几个点:

  • ref 的 .value 忘记写:在 setup 函数里修改 ref 变量时,必须写 .valuecount.value++,但模板里不用写(Vue 自动帮你解包);
  • reactive 赋值丢响应式:如果给 reactive 变量整个替换(user = { name: '新名字' }),原来的响应式会失效,改成用 ref 包对象更安全;
  • 生命周期钩子导错:Vue3 里生命周期钩子要从 vue 导入,onMounted 代替 mounted,忘记导入会报错;
  • this 指向丢了:Composition API 里没有 this<script setup> 是无 this 模式),如果硬要在方法里用 this,得换回 Options API 写法,否则会踩坑;
  • 自定义 Hook 里的响应式丢失:Hook 里返回的是普通变量(不是 ref/reactive),组件里拿不到响应式更新,Hook 里要返回 ref/reactive 包装后的值。

最后补一句:Composition API 不是要完全替代 Options API,而是给复杂场景提供更优解,小项目用 Options 写得快,中大型项目用 Composition 更易维护,关键是理解它“聚合逻辑、灵活复用”的设计思想,用对场景才是王道~

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

热门