Vue 组合式 API(Composition API)基础(Vue 3 核心)详解

举报
William 发表于 2025/10/09 12:38:48 2025/10/09
【摘要】 一、引言在 Vue.js 的发展历程中,​​选项式 API(Options API)​​ 一直是核心的开发模式(如 Vue 2 的 data、methods、computed等选项)。但随着应用复杂度的提升,选项式 API 逐渐暴露出一些局限性:​​逻辑复用困难​​:相关逻辑(如数据获取、事件处理)分散在 data、methods、computed等不同选项中,难以提取和复用;​​代码组织不...


一、引言

在 Vue.js 的发展历程中,​​选项式 API(Options API)​​ 一直是核心的开发模式(如 Vue 2 的 datamethodscomputed等选项)。但随着应用复杂度的提升,选项式 API 逐渐暴露出一些局限性:
  • ​逻辑复用困难​​:相关逻辑(如数据获取、事件处理)分散在 datamethodscomputed等不同选项中,难以提取和复用;
  • ​代码组织不直观​​:大型组件中,选项式 API 的代码按功能类型拆分(如 data定义数据、methods定义方法),但实际业务逻辑可能是跨功能的(如“用户信息管理”涉及数据、操作、计算),导致代码可读性和维护性降低;
  • ​TypeScript 支持较弱​​:选项式 API 的类型推断需要额外配置,对 TypeScript 的友好度不足。
为了解决这些问题,​​Vue 3 引入了组合式 API(Composition API)​​——它是一套基于函数的 API,允许开发者通过 ​​逻辑聚合​​ 的方式组织代码,将相关的功能(如数据、方法、计算属性)封装到一个或多个函数中,从而提升代码的可复用性、可维护性和 TypeScript 支持。
本文将围绕组合式 API 的基础概念、核心语法、应用场景、代码实现、原理解释到实战演示,全方位解析其作为 Vue 3 核心特性的价值与使用方法。

二、技术背景

1. 选项式 API 的局限性

在 Vue 2 的选项式 API 中,组件的逻辑通过以下选项定义:
  • data​:定义响应式数据(如用户信息、表单输入值);
  • methods​:定义组件的方法(如事件处理函数、数据操作函数);
  • computed​:定义计算属性(基于其他数据动态计算的值);
  • watch​:监听数据变化并执行副作用(如异步请求、DOM 操作);
  • 生命周期钩子​(如 createdmounted):在组件不同阶段执行逻辑。
​问题示例​​:假设我们需要开发一个“用户信息管理”组件,包含数据获取、表单提交、状态管理等功能。在选项式 API 中,相关逻辑可能分散在多个选项中:
export default {
  data() {
    return {
      user: null,       // 用户数据
      loading: false,   // 加载状态
      error: null,      // 错误信息
    };
  },
  methods: {
    async fetchUser() { // 获取用户数据(方法)
      this.loading = true;
      try {
        const res = await api.getUser(); // 假设的 API 调用
        this.user = res.data;
      } catch (err) {
        this.error = err.message;
      } finally {
        this.loading = false;
      }
    },
    submitForm() { /* 提交表单逻辑 */ }
  },
  created() { // 生命周期钩子
    this.fetchUser(); // 组件创建时获取用户
  }
};
​缺点​​:
  • 相关逻辑(如 fetchUser方法、loading/error状态、created钩子中的调用)分散在不同选项中,难以快速定位和复用;
  • 若需要在多个组件中复用“获取用户数据”的逻辑,需通过 Mixin 或高阶组件实现,但会导致命名冲突和类型推断困难。

2. 组合式 API 的核心思想

组合式 API 的设计目标是 ​​让开发者以逻辑为核心组织代码​​,而不是按功能类型(数据、方法、生命周期)拆分。它通过以下特性解决问题:
  • ​基于函数的 API​​:提供 refreactivecomputedwatchonMounted等函数,用于定义响应式数据、计算属性、监听器和生命周期逻辑;
  • ​逻辑聚合​​:将相关的功能封装到一个或多个自定义函数(称为“组合函数”)中,组件只需调用这些函数即可复用逻辑;
  • ​更好的 TypeScript 支持​​:函数的参数和返回值具有明确的类型,能自动推断组件的状态和方法类型;
  • ​更灵活的代码组织​​:开发者可以根据业务逻辑(如“用户管理”“表单处理”)而非选项类型(如 data/methods)组织代码,提升可读性。

三、应用使用场景

1. 复杂组件的逻辑复用

  • ​场景​​:多个组件需要共享相同的功能(如“用户认证”“数据分页”“表单验证”);
  • ​需求​​:通过组合式 API 将这些功能封装成独立的组合函数(如 useAuthusePagination),在需要的组件中直接调用,避免重复代码。

2. 大型组件的代码组织

  • ​场景​​:单个组件包含大量逻辑(如电商详情页包含商品信息、库存管理、用户评价、购物车操作);
  • ​需求​​:将不同功能的逻辑(如商品数据获取、评价列表渲染、购物车更新)拆分成多个组合函数,每个函数专注单一职责,提升代码可维护性。

3. TypeScript 项目的类型安全

  • ​场景​​:使用 TypeScript 开发 Vue 应用,需要明确的类型推断和类型检查;
  • ​需求​​:组合式 API 的函数(如 ref<T>reactive<T>)支持泛型,能自动推断组件状态的类型,减少类型定义的冗余代码。

4. 逻辑跨组件复用

  • ​场景​​:多个组件需要使用相同的工具逻辑(如“窗口大小监听”“本地存储管理”“API 请求封装”);
  • ​需求​​:通过组合函数(如 useWindowSizeuseLocalStorage)封装这些工具逻辑,在不同组件中复用,避免重复实现。

四、不同场景下详细代码实现

场景 1:基础响应式数据与生命周期(Vue 3 组合式 API)

​需求​​:封装一个简单的计数器组件(Counter.vue),通过组合式 API 定义响应式数据(count)、方法(increment)和生命周期钩子(onMounted)。

1.1 计数器组件(Counter.vue)

<template>
  <div class="counter">
    <h3>组合式 API 基础示例(计数器)</h3>
    <p>当前计数:{{ count }}</p>
    <button @click="increment">+1</button>
    <button @click="decrement">-1</button>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';

// 定义响应式数据(类似 Options API 的 data)
const count = ref(0); // 初始值为 0

// 定义方法(类似 Options API 的 methods)
const increment = () => {
  count.value++; // ref 的值需通过 .value 访问
};

const decrement = () => {
  count.value--;
};

// 生命周期钩子(类似 Options API 的 mounted)
onMounted(() => {
  console.log('✅ 组件已挂载到 DOM,当前计数:', count.value);
});
</script>

<style scoped>
.counter {
  padding: 16px;
  border: 1px solid #eee;
  border-radius: 8px;
  margin: 10px;
}
button {
  margin: 0 8px;
  padding: 8px 16px;
  background: #007bff;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
</style>
​运行结果​​:
  • 页面显示一个计数器,点击“+1”或“-1”按钮时,count的值实时更新;
  • 打开浏览器控制台(F12),组件挂载后输出日志 ✅ 组件已挂载到 DOM,当前计数:0

场景 2:逻辑复用(自定义组合函数)

​需求​​:封装一个“用户信息获取”的组合函数(useUser.js),在多个组件中复用获取用户数据的逻辑(如用户详情页、用户列表页)。

2.1 自定义组合函数(useUser.js)

// src/composables/useUser.js
import { ref, onMounted } from 'vue';

// 定义组合函数:封装获取用户数据的逻辑
export function useUser() {
  // 响应式数据:用户信息和加载状态
  const user = ref(null);
  const loading = ref(false);
  const error = ref(null);

  // 方法:获取用户数据(模拟 API 调用)
  const fetchUser = async (userId) => {
    loading.value = true;
    error.value = null;
    try {
      // 模拟异步请求(实际项目中替换为真实 API 调用)
      const response = await new Promise((resolve) => {
        setTimeout(() => {
          resolve({ data: { id: userId, name: `用户${userId}`, age: 20 + userId } });
        }, 1000);
      });
      user.value = response.data;
    } catch (err) {
      error.value = '获取用户数据失败:' + err.message;
    } finally {
      loading.value = false;
    }
  };

  // 生命周期:组件挂载时自动获取用户(可选,根据需求调用)
  // 注意:实际使用时,通常由组件手动调用 fetchUser(userId)

  // 返回响应式数据和方法,供组件使用
  return {
    user,
    loading,
    error,
    fetchUser,
  };
}

2.2 使用组合函数的组件(UserDetail.vue)

<template>
  <div class="user-detail">
    <h3>用户详情(使用组合函数 useUser)</h3>
    <button @click="loadUser(1)">加载用户 1</button>
    <button @click="loadUser(2)">加载用户 2</button>
    
    <!-- 加载状态 -->
    <p v-if="loading">加载中...</p>
    
    <!-- 错误提示 -->
    <p v-else-if="error" style="color: red;">错误:{{ error }}</p>
    
    <!-- 用户信息 -->
    <div v-else-if="user">
      <p><strong>ID:</strong>{{ user.id }}</p>
      <p><strong>姓名:</strong>{{ user.name }}</p>
      <p><strong>年龄:</strong>{{ user.age }}</p>
    </div>
    
    <!-- 未加载时的提示 -->
    <p v-else>点击按钮加载用户信息</p>
  </div>
</template>

<script setup>
import { useUser } from '@/composables/useUser'; // 导入自定义组合函数

// 调用组合函数,获取响应式数据和方法
const { user, loading, error, fetchUser } = useUser();

// 组件内定义方法:触发用户数据获取
const loadUser = (userId) => {
  fetchUser(userId); // 调用组合函数中的方法
};
</script>

<style scoped>
.user-detail {
  padding: 16px;
  border: 1px solid #eee;
  border-radius: 8px;
  margin: 10px;
}
button {
  margin: 0 8px 10px 0;
  padding: 8px 16px;
  background: #28a745;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
</style>
​运行结果​​:
  • 点击“加载用户 1”或“加载用户 2”按钮,触发 fetchUser方法,模拟异步请求后显示用户信息(ID、姓名、年龄);
  • 加载过程中显示“加载中...”,出错时显示错误提示,逻辑完全复用自 useUser组合函数。

场景 3:计算属性与监听器(组合式 API)

​需求​​:封装一个“购物车”组件(Cart.vue),通过组合式 API 的 computed(计算属性)计算总价,通过 watch(监听器)监听商品列表变化并输出日志。

3.1 购物车组件(Cart.vue)

<template>
  <div class="cart">
    <h3>购物车(组合式 API:计算属性与监听器)</h3>
    <div v-for="item in cartItems" :key="item.id" class="cart-item">
      <span>{{ item.name }} - ¥{{ item.price }} × {{ item.quantity }}</span>
      <button @click="updateQuantity(item.id, item.quantity + 1)">+1</button>
      <button @click="updateQuantity(item.id, item.quantity - 1)">-1</button>
    </div>
    <p><strong>总价:¥{{ totalPrice }}</strong></p>
  </div>
</template>

<script setup>
import { ref, computed, watch } from 'vue';

// 响应式数据:购物车商品列表
const cartItems = ref([
  { id: 1, name: '苹果', price: 5, quantity: 2 },
  { id: 2, name: '香蕉', price: 3, quantity: 1 },
]);

// 计算属性:总价(基于 cartItems 动态计算)
const totalPrice = computed(() => {
  return cartItems.value.reduce((sum, item) => sum + (item.price * item.quantity), 0);
});

// 监听器:监听购物车商品列表变化(如数量修改)
watch(cartItems, (newItems) => {
  console.log('🛒 购物车商品列表已更新,当前商品:', newItems);
}, { deep: true }); // 深度监听(因为商品对象的 quantity 可能变化)

// 方法:更新商品数量
const updateQuantity = (id, newQuantity) => {
  if (newQuantity < 0) newQuantity = 0; // 数量不能小于 0
  const item = cartItems.value.find(item => item.id === id);
  if (item) {
    item.quantity = newQuantity;
  }
};
</script>

<style scoped>
.cart {
  padding: 16px;
  border: 1px solid #eee;
  border-radius: 8px;
  margin: 10px;
}
.cart-item {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 8px;
  padding: 8px;
  background: #f9f9f9;
  border-radius: 4px;
}
button {
  padding: 4px 8px;
  background: #007bff;
  color: white;
  border: none;
  border-radius: 2px;
  cursor: pointer;
}
</style>
​运行结果​​:
  • 修改商品数量(点击“+1”或“-1”按钮)时,总价(totalPrice)实时更新;
  • 控制台输出监听日志,显示购物车商品列表的变化(如数量修改后的新列表)。

五、原理解释

1. 组合式 API 的核心机制

组合式 API 的本质是通过 ​​函数​​ 提供 Vue 的核心功能(如响应式数据、生命周期、计算属性),开发者可以将这些函数组合起来,按逻辑相关性组织代码。其核心原理如下:

1.1 响应式数据(refreactive

  • ref​:用于定义 ​​基本类型或对象引用​​ 的响应式数据(如 const count = ref(0))。访问或修改 ref的值时,需通过 .value属性(如 count.value++)。
  • reactive​:用于定义 ​​对象类型的响应式数据​​(如 const state = reactive({ user: null }))。访问或修改 reactive的属性时,可直接通过属性名(如 state.user.name = '张三')。

1.2 生命周期钩子(onXxx函数)

组合式 API 提供了一系列 onXxx函数,用于替代选项式 API 的生命周期钩子:
  • onBeforeMount→ 类似 beforeMount(组件即将挂载)
  • onMounted→ 类似 mounted(组件已挂载到 DOM)
  • onBeforeUpdate→ 类似 beforeUpdate(组件即将更新)
  • onUpdated→ 类似 updated(组件已更新)
  • onBeforeUnmount→ 类似 beforeDestroy(组件即将销毁)
  • onUnmounted→ 类似 destroyed(组件已销毁)

1.3 计算属性(computed)与监听器(watch

  • computed​:用于定义 ​​基于其他响应式数据动态计算的值​​(如总价基于商品数量和价格)。返回一个只读的响应式引用(需通过 .value访问)。
  • watch​:用于 ​​监听响应式数据的变化并执行副作用​​(如数据变化时发送请求、输出日志)。支持监听单个数据或对象,可配置深度监听(deep: true)。

2. 逻辑聚合的核心优势

组合式 API 的核心优势在于 ​​逻辑聚合​​——将相关的功能(如数据获取、状态管理、方法定义)封装到一个函数或代码块中,而不是分散在 datamethods生命周期等不同选项中。例如:
  • ​传统选项式 API​​:数据(data)、方法(methods)、生命周期(created)分散在不同选项,逻辑关联性弱;
  • ​组合式 API​​:通过 setup()函数(或 <script setup>语法糖)将相关的数据(ref)、方法(普通函数)、生命周期(onMounted)组织在一起,逻辑更集中。

六、核心特性

特性
说明
​逻辑聚合​
将相关的功能(数据、方法、计算属性)封装到一起,提升代码可读性和复用性;
​基于函数的 API​
通过 refreactivecomputedwatch等函数定义响应式逻辑;
​更好的 TypeScript 支持​
函数参数和返回值具有明确类型,能自动推断组件状态和方法类型;
​灵活的代码组织​
开发者可根据业务逻辑(如“用户管理”“表单处理”)组织代码,而非选项类型;
​逻辑复用​
通过自定义组合函数(如 useUseruseCart)封装通用逻辑,在多个组件中复用;
​生命周期控制​
通过 onXxx函数精确控制组件在不同阶段的逻辑执行;

七、原理流程图及原理解释

原理流程图(组合式 API 执行流程)

+-----------------------+
|     组件初始化        |
|  (setup() 函数执行)   |  --> Vue 3 中组合式 API 的入口(<script setup> 隐式包含 setup)
+-----------------------+
          |
          v
+-----------------------+
| 定义响应式数据        |  --> 通过 ref() 或 reactive() 定义(如 count = ref(0))
+-----------------------+
          |
          v
+-----------------------+
| 定义方法              |  --> 普通函数(如 increment() { count.value++ })
+-----------------------+
          |
          v
+-----------------------+
| 注册生命周期钩子      |  --> 通过 onMounted()、onUpdated() 等函数(如 onMounted(() => {}))
+-----------------------+
          |
          v
+-----------------------+
| 计算属性与监听器      |  --> 通过 computed() 和 watch() 定义(如 totalPrice = computed(...))
+-----------------------+
          |
          v
+-----------------------+
| 组件渲染与交互        |  --> 模板中通过响应式数据和方法实现动态更新
+-----------------------+

原理解释

  1. ​组件初始化​​:Vue 3 在创建组件时,会优先执行 <script setup>中的代码(或显式的 setup()函数),这是组合式 API 的入口;
  2. ​响应式数据定义​​:通过 refreactive定义组件的状态(如 countuser),这些数据会被 Vue 的响应式系统追踪;
  3. ​方法定义​​:普通函数(如 increment)可以直接操作响应式数据(通过 .value访问 ref的值);
  4. ​生命周期注册​​:通过 onMountedonUpdated等函数注册生命周期逻辑,在组件对应阶段执行(如 onMounted中发送初始请求);
  5. ​计算属性与监听器​​:computed用于定义依赖其他数据的动态值(如总价),watch用于监听数据变化并执行副作用(如日志输出);
  6. ​渲染与交互​​:模板中通过响应式数据和方法实现动态更新,当数据变化时,Vue 自动重新渲染相关部分。

八、环境准备

1. 开发环境

  • ​Vue 3​​:组合式 API 是 Vue 3 的核心特性(Vue 2 需通过插件 @vue/composition-api实现,但不推荐);
  • ​开发工具​​:Vue CLI 或 Vite(推荐 Vite,启动更快);
  • ​构建工具​​:确保项目配置支持 ES Modules 和现代 JavaScript 语法。

2. 项目配置

  • 若使用 TypeScript,确保 tsconfig.json中启用了严格的类型检查(如 "strict": true),以充分利用组合式 API 的类型推断;
  • 推荐使用 <script setup>语法糖(更简洁,无需显式返回数据和方法)。

九、实际详细应用代码示例实现

完整项目代码(整合上述场景)

1. 计数器组件(Counter.vue)

(代码同场景 1)

2. 自定义组合函数(useUser.js)与用户详情组件(UserDetail.vue)

(代码同场景 2)

3. 购物车组件(Cart.vue)

(代码同场景 3)

4. 主应用(App.vue)

<template>
  <div id="app">
    <h1>Vue 3 组合式 API 基础示例</h1>
    
    <!-- 场景 1:计数器 -->
    <section>
      <h2>场景 1:计数器</h2>
      <Counter />
    </section>

    <!-- 场景 2:用户详情(使用组合函数) -->
    <section>
      <h2>场景 2:用户详情(逻辑复用)</h2>
      <UserDetail />
    </section>

    <!-- 场景 3:购物车 -->
    <section>
      <h2>场景 3:购物车(计算属性与监听器)</h2>
      <Cart />
    </section>
  </div>
</template>

<script setup>
import Counter from './components/Counter.vue';
import UserDetail from './components/UserDetail.vue';
import Cart from './components/Cart.vue';
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  padding: 20px;
  max-width: 800px;
  margin: 0 auto;
}
section {
  margin-bottom: 40px;
  border: 1px solid #eee;
  padding: 20px;
  border-radius: 8px;
}
</style>
​运行结果​​:
  • 页面包含三个场景的组件(计数器、用户详情、购物车),验证组合式 API 的基础功能(响应式数据、生命周期、逻辑复用、计算属性)。

十、运行结果

1. 计数器的表现

  • 点击“+1”或“-1”按钮时,计数实时更新;
  • 控制台输出组件挂载日志。

2. 用户详情的表现

  • 点击“加载用户 1”或“加载用户 2”按钮,模拟异步请求后显示用户信息(ID、姓名、年龄);
  • 加载过程中显示“加载中...”,出错时显示错误提示。

3. 购物车的表现

  • 修改商品数量时,总价实时更新;
  • 控制台输出购物车商品列表的变化日志。

十一、测试步骤以及详细代码

1. 测试目标

验证组合式 API 的核心功能,包括:
  • 响应式数据(ref)是否能正确更新并触发视图渲染;
  • 生命周期钩子(onMounted)是否在组件挂载时执行;
  • 逻辑复用(自定义组合函数 useUser)是否能在多个组件中复用;
  • 计算属性(computed)和监听器(watch)是否能按预期工作。

2. 测试步骤

步骤 1:启动项目

npm run serve  # Vue 2(若使用 Vue 3 推荐 npm run dev 或 vite 启动)
# 或 npm run dev  # Vue 3 (Vite)
访问 http://localhost:5173(Vite 默认端口),查看各场景组件。

步骤 2:测试计数器

  • 点击“+1”和“-1”按钮,观察计数是否实时更新;
  • 检查控制台是否有组件挂载日志。

步骤 3:测试用户详情

  • 点击“加载用户 1”和“加载用户 2”按钮,观察用户信息是否正确显示;
  • 检查加载状态和错误提示是否按预期出现。

步骤 4:测试购物车

  • 修改商品数量(点击“+1”或“-1”),观察总价是否实时计算;
  • 检查控制台是否有购物车商品列表变化的日志输出。

十二、部署场景

1. 生产环境注意事项

  • ​响应式数据优化​​:避免在组合函数中定义过多全局响应式数据(可能导致内存占用过高);
  • ​生命周期管理​​:在 onUnmounted中清理定时器、取消网络请求、移除事件监听器,避免内存泄漏;
  • ​TypeScript 类型安全​​:为组合函数定义明确的类型(如泛型 ref<T>),提升代码健壮性;
  • ​逻辑复用规范​​:将常用的组合函数(如 useUseruseCart)放在 src/composables/目录下,统一管理。

2. 适用场景

  • ​复杂业务逻辑​​:如用户认证、数据分页、表单验证;
  • ​跨组件复用​​:如工具函数(本地存储、窗口大小监听)、API 请求封装;
  • ​**​大型组件拆分
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。