掌握 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,定义的变量 / 方法直接暴露给模板 - 自动注册导入的组件(后面组件通信会演示)
- 内置
defineProps、defineEmits等函数,无需导入 - 顶层 await 支持(后面异步请求会演示)
二、组合式 API 核心能力:响应式数据处理
组合式 API 通过 ref、reactive 等 API 创建响应式数据,替代 Vue2 选项式 API 的 data(),这是最基础也最核心的能力。
1. 基本类型响应式:ref
用于 String、Number、Boolean 等基本类型,核心是「通过 .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 |
toRefs | reactive 对象 | 解构后 .value 修改 | 解构不丢失响应式 |
三、组合式 API 常用能力:生命周期、计算属性、方法
1. 生命周期钩子(替代选项式 API 的钩子函数)
组合式 API 的生命周期钩子需要「按需导入」,名称以 on 开头,核心钩子对应关系如下:
| 选项式 API | 组合式 API(setup 中) | 用途 |
|---|---|---|
mounted | onMounted | 组件挂载完成(可操作 DOM) |
updated | onUpdated | 组件数据更新后 |
unmounted | onUnmounted | 组件卸载前(清理资源) |
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> 内置的 defineProps 和 defineEmits。
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 个关键点:
- 入口:通过
<script setup>语法糖开启,无需导出,简洁高效; - 核心:用
ref(基本类型)/reactive(复杂类型)创建响应式数据,配合computed、生命周期钩子完成业务逻辑; - 优势:通过「组合函数(useXXX)」抽离通用逻辑,实现跨组件复用,解决选项式 API 逻辑分散的问题。
实际开发中,建议:
- 优先使用
<script setup>语法糖; - 基本类型用
ref,复杂类型用reactive; - 通用逻辑(如请求、表单、计数器)抽成组合函数,提升代码复用率;
- 组件通信仍以
props+emit为主,全局状态用 Pinia(后续可深入学习)。
多动手写示例(比如计数器、 TodoList、数据请求),很快就能熟练掌握组合式 API 的用法!
3226

被折叠的 条评论
为什么被折叠?



