如何在Vue3中使用组合式API?

掌握 Vue3 组合式 API 的具体使用方法,这是 Vue3 的核心特性,能让代码逻辑更集中、复用更灵活!我会从「基础用法→核心 API→实际场景」一步步带你落地,每个知识点都配可直接运行的代码示例,帮你快速上手。

一、组合式 API 的核心入口:<script setup>

组合式 API 的最佳实践是配合 <script setup> 语法糖(Vue3.2 + 支持),它无需手动导出组件,内置了很多便捷函数,是日常开发的首选。

基本结构(Hello World 级示例)

<template>
  <div>
    <h1>{{ msg }}</h1>
    <p>计数:{{ count }}</p>
    <button @click="addCount">+1</button>
  </div>
</template>

<!-- 关键:添加 setup 属性 -->
<script setup>
// 1. 导入需要的Vue API(按需导入,减小打包体积)
import { ref } from 'vue'

// 2. 响应式数据(ref用于基本类型)
const count = ref(0) // 初始值0,ref包裹后成为响应式对象
const msg = ref('Hello 组合式API')

// 3. 方法(直接定义,模板中可直接使用)
const addCount = () => {
  // 注意:ref类型的响应式数据,修改时需通过 .value 访问
  count.value++
  if (count.value === 5) {
    msg.value = '计数达到5啦!'
  }
}
</script>

核心特点:

  • 无需 export default,定义的变量 / 方法直接暴露给模板
  • 自动注册导入的组件(后面组件通信会演示)
  • 内置 definePropsdefineEmits 等函数,无需导入
  • 顶层 await 支持(后面异步请求会演示)

二、组合式 API 核心能力:响应式数据处理

组合式 API 通过 refreactive 等 API 创建响应式数据,替代 Vue2 选项式 API 的 data(),这是最基础也最核心的能力。

1. 基本类型响应式:ref

用于 StringNumberBoolean 等基本类型,核心是「通过 .value 访问 / 修改」。

<script setup>
import { ref } from 'vue'

// 1. 定义基本类型响应式数据
const username = ref('张三')
const age = ref(18)
const isStudent = ref(true)

// 2. 修改响应式数据(必须加 .value)
const updateUser = () => {
  username.value = '李四'
  age.value = 20
  isStudent.value = false
}

// 3. 模板中使用时,无需 .value(Vue自动解析)
// 模板中写:{{ username }} → 直接显示「张三」
</script>

2. 对象 / 数组响应式:reactive

用于复杂类型(对象、数组),无需 .value,直接修改属性即可。

<script setup>
import { reactive } from 'vue'

// 1. 定义对象类型响应式数据
const user = reactive({
  name: '张三',
  address: {
    city: '北京',
    area: '朝阳区'
  }
})

// 2. 定义数组类型响应式数据
const hobbyList = reactive(['篮球', '游戏', '看书'])

// 3. 修改数据(直接操作属性/数组方法)
const updateUserInfo = () => {
  user.name = '李四' // 直接修改对象属性
  user.address.city = '上海' // 深层对象也支持响应式
  hobbyList.push('旅行') // 数组方法操作(push/pop/splice等)
}
</script>

3. 解构不丢失响应式:toRefs

如果想解构 reactive 对象(比如 const { name } = user),直接解构会丢失响应式,此时用 toRefs 转换:

<script setup>
import { reactive, toRefs } from 'vue'

const user = reactive({
  name: '张三',
  age: 18
})

// 关键:用 toRefs 解构,保持响应式
const { name, age } = toRefs(user)

// 修改时,需要 .value(因为toRefs把属性转为了ref类型)
const updateName = () => {
  name.value = '李四' // 此时user.name也会同步更新,响应式不丢失
}
</script>

4. 响应式数据对比总结

API适用类型访问 / 修改方式核心特点
ref基本类型(含数组)模板:直接用,逻辑:.value简单通用,支持所有类型
reactive对象 / 数组直接操作属性 / 方法符合直觉,无需 .value
toRefsreactive 对象解构后 .value 修改解构不丢失响应式

三、组合式 API 常用能力:生命周期、计算属性、方法

1. 生命周期钩子(替代选项式 API 的钩子函数)

组合式 API 的生命周期钩子需要「按需导入」,名称以 on 开头,核心钩子对应关系如下:

选项式 API组合式 API(setup 中)用途
mountedonMounted组件挂载完成(可操作 DOM)
updatedonUpdated组件数据更新后
unmountedonUnmounted组件卸载前(清理资源)
created-(setup 中直接执行)组件创建后(无需钩子)

使用示例

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

const count = ref(0)

// 组件挂载完成后执行(类似Vue2的mounted)
onMounted(() => {
  console.log('组件挂载完成,DOM已渲染')
  // 比如初始化图表、绑定事件监听
})

// 数据更新后执行(类似Vue2的updated)
onUpdated(() => {
  console.log('count更新为:', count.value)
})

// 组件卸载前执行(类似Vue2的beforeDestroy)
onUnmounted(() => {
  console.log('组件即将卸载,清理资源')
  // 比如清除定时器、解绑事件
  clearInterval(timer)
})

// 定时器示例(需要在卸载时清理)
const timer = setInterval(() => {
  count.value++
}, 1000)
</script>

2. 计算属性:computed

替代选项式 API 的 computed,用于依赖响应式数据的「派生值」,缓存结果提升性能。

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

const count = ref(0)

// 定义计算属性:count的2倍
const doubleCount = computed(() => {
  return count.value * 2
})

// 定义可修改的计算属性(需要get/set)
const fullName = computed({
  // 获取值
  get() {
    return `${firstName.value} ${lastName.value}`
  },
  // 修改值(触发时更新依赖)
  set(newValue) {
    const [first, last] = newValue.split(' ')
    firstName.value = first
    lastName.value = last
  }
})

const firstName = ref('张')
const lastName = ref('三')

// 修改计算属性(会触发set方法)
const updateFullName = () => {
  fullName.value = '李 四'
}
</script>

<template>
  <p>count: {{ count }}</p>
  <p>count的2倍:{{ doubleCount }}</p>
  <p>全名:{{ fullName }}</p>
  <button @click="updateFullName">修改全名</button>
</template>

3. 方法定义(直接定义函数)

组合式 API 中,方法无需写在 methods 选项里,直接在 <script setup> 中定义函数即可,模板中可直接调用:

<script setup>
import { ref } from 'vue'

const count = ref(0)

// 直接定义方法
const addCount = () => {
  count.value++
}

// 带参数的方法
const sayHello = (name) => {
  alert(`Hello ${name}!`)
}
</script>

<template>
  <button @click="addCount">+1</button>
  <button @click="sayHello('Vue3')">打招呼</button>
</template>

四、组合式 API 核心优势:逻辑复用(组合函数)

这是组合式 API 最强大的功能!可以把通用逻辑(比如计数器、请求数据、表单验证)提取成「组合函数」(以 use 开头命名),在多个组件中复用。

示例 1:提取计数器逻辑(useCounter.js

// src/hooks/useCounter.js(通用逻辑抽离)
import { ref, computed } from 'vue'

// 组合函数:以use开头,返回需要暴露的变量/方法
export const useCounter = (initialValue = 0) => {
  // 响应式数据
  const count = ref(initialValue)

  // 计算属性
  const doubleCount = computed(() => count.value * 2)

  // 方法
  const add = () => count.value++
  const subtract = () => count.value--
  const reset = () => count.value = initialValue

  // 暴露给组件使用
  return {
    count,
    doubleCount,
    add,
    subtract,
    reset
  }
}

组件中复用逻辑:

<template>
  <div>
    <h3>计数器1(初始值0)</h3>
    <p>count: {{ count1 }}</p>
    <p>doubleCount: {{ doubleCount1 }}</p>
    <button @click="add1">+1</button>
    <button @click="reset1">重置</button>

    <h3>计数器2(初始值10)</h3>
    <p>count: {{ count2 }}</p>
    <p>doubleCount: {{ doubleCount2 }}</p>
    <button @click="add2">+1</button>
    <button @click="subtract2">-1</button>
  </div>
</template>

<script setup>
// 导入组合函数
import { useCounter } from '@/hooks/useCounter'

// 复用逻辑:创建两个独立的计数器(互不影响)
const { count: count1, doubleCount: doubleCount1, add: add1, reset: reset1 } = useCounter(0)
const { count: count2, doubleCount: doubleCount2, add: add2, subtract: subtract2 } = useCounter(10)
</script>

示例 2:提取异步请求逻辑(useFetch.js

// src/hooks/useFetch.js
import { ref, onUnmounted } from 'vue'

export const useFetch = (url) => {
  const data = ref(null) // 接口返回数据
  const loading = ref(false) // 加载状态
  const error = ref(null) // 错误信息
  const controller = new AbortController() // 用于取消请求

  // 发送请求
  const fetchData = async () => {
    loading.value = true
    error.value = null
    try {
      const res = await fetch(url, { signal: controller.signal })
      if (!res.ok) throw new Error('请求失败')
      data.value = await res.json()
    } catch (err) {
      if (err.name !== 'AbortError') { // 忽略取消请求的错误
        error.value = err.message
      }
    } finally {
      loading.value = false
    }
  }

  // 组件挂载时自动请求
  fetchData()

  // 组件卸载时取消请求(避免内存泄漏)
  onUnmounted(() => {
    controller.abort()
  })

  return {
    data,
    loading,
    error,
    refetch: fetchData // 提供重新请求的方法
  }
}

组件中复用请求逻辑:

<template>
  <div>
    <div v-if="loading">加载中...</div>
    <div v-else-if="error">错误:{{ error }}</div>
    <div v-else-if="data">
      <h3>用户列表</h3>
      <ul>
        <li v-for="user in data" :key="user.id">{{ user.name }}</li>
      </ul>
      <button @click="refetch">重新加载</button>
    </div>
  </div>
</template>

<script setup>
import { useFetch } from '@/hooks/useFetch'

// 复用异步请求逻辑
const { data, loading, error, refetch } = useFetch('https://jsonplaceholder.typicode.com/users')
</script>

五、组合式 API 中的组件通信

组合式 API 的组件通信逻辑更简洁,核心还是 props(父传子)和 emit(子传父),配合 <script setup> 内置的 definePropsdefineEmits

1. 父传子:defineProps

<!-- 父组件 Parent.vue -->
<template>
  <!-- 传递参数给子组件 -->
  <Child :title="parentTitle" :count="10" />
</template>

<script setup>
import Child from './Child.vue'
import { ref } from 'vue'

const parentTitle = ref('父组件传递的标题')
</script>
<!-- 子组件 Child.vue -->
<template>
  <div>
    <h3>{{ title }}</h3>
    <p>父组件传递的count:{{ count }}</p>
  </div>
</template>

<script setup>
// 接收父组件参数(无需导入,内置函数)
const props = defineProps({
  title: {
    type: String,
    required: true,
    default: '默认标题'
  },
  count: {
    type: Number,
    default: 0
  }
})

// 访问props:直接用 props.title 或解构(需配合toRefs)
// 解构示例:const { title, count } = toRefs(props)
</script>

2. 子传父:defineEmits

<!-- 子组件 Child.vue -->
<template>
  <button @click="handleClick">向父组件传值</button>
</template>

<script setup>
// 声明要触发的事件(内置函数,无需导入)
const emit = defineEmits(['child-click', 'update-title'])

const handleClick = () => {
  // 触发事件并传递参数(第一个参数是事件名,后面是参数)
  emit('child-click', '子组件传递的数据')
  emit('update-title', '子组件修改后的标题')
}
</script>
<!-- 父组件 Parent.vue -->
<template>
  <Child 
    :title="parentTitle" 
    @child-click="handleChildClick" 
    @update-title="updateParentTitle"
  />
</template>

<script setup>
import Child from './Child.vue'
import { ref } from 'vue'

const parentTitle = ref('父组件传递的标题')

// 接收子组件事件
const handleChildClick = (data) => {
  console.log('接收子组件数据:', data) // 输出:子组件传递的数据
}

// 接收子组件修改请求
const updateParentTitle = (newTitle) => {
  parentTitle.value = newTitle // 更新父组件数据
}
</script>

总结

组合式 API 的使用核心可概括为 3 个关键点:

  1. 入口:通过 <script setup> 语法糖开启,无需导出,简洁高效;
  2. 核心:用 ref(基本类型)/reactive(复杂类型)创建响应式数据,配合 computed、生命周期钩子完成业务逻辑;
  3. 优势:通过「组合函数(useXXX)」抽离通用逻辑,实现跨组件复用,解决选项式 API 逻辑分散的问题。

实际开发中,建议:

  • 优先使用 <script setup> 语法糖;
  • 基本类型用 ref,复杂类型用 reactive
  • 通用逻辑(如请求、表单、计数器)抽成组合函数,提升代码复用率;
  • 组件通信仍以 props + emit 为主,全局状态用 Pinia(后续可深入学习)。

多动手写示例(比如计数器、 TodoList、数据请求),很快就能熟练掌握组合式 API 的用法!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

涔溪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值