Vue 3 组合式 API 深度解析与实战
1. 引言
Vue 3 的组合式 API (Composition API) 是 Vue 3 中最核心、最强大的特性之一,它彻底改变了 Vue 应用的代码组织方式和逻辑复用模式。与传统的选项式 API (Options API) 相比,组合式 API 提供了更灵活、更可维护的代码组织方式,特别适合构建大型复杂的 Vue 应用。
本文将深入解析 Vue 3 组合式 API 的设计原理、使用方法、最佳实践,并通过实战案例展示其在实际项目中的应用。我们将从基础概念开始,逐步深入到高级用法,帮助你掌握组合式 API 的精髓。
2. 组合式 API 基础
2.1 什么是组合式 API
组合式 API 是 Vue 3 中新增的一组 API,它允许我们以函数式的方式组织组件逻辑,而不是像选项式 API 那样通过选项(如 data、computed、methods 等)来组织。
核心优势:
- 更好的逻辑复用:通过函数方式组织逻辑,可在不同组件间轻松复用
- 更清晰的代码组织:相关逻辑可以放在一起,提高代码可读性和可维护性
- 更好的类型推断:与 TypeScript 配合使用时,提供更精确的类型推断
- 更灵活的代码结构:不再受选项式 API 的限制,可以根据逻辑关系组织代码
2.2 组合式 API 的基本使用
setup 函数
setup 函数是组合式 API 的入口点,它在组件创建之前执行,用于初始化组件的状态和逻辑。
vue
<template>
<div>
<h1>{{ title }}</h1>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script>
import { ref, computed, onMounted } from 'vue'
export default {
setup() {
// 响应式状态
const count = ref(0)
const title = ref('Vue 3 Composition API Example')
// 计算属性
const doubleCount = computed(() => count.value * 2)
// 方法
function increment() {
count.value++
}
// 生命周期钩子
onMounted(() => {
console.log('Component mounted')
})
// 返回暴露给模板的内容
return {
count,
title,
doubleCount,
increment
}
}
}
</script>
<script setup> 语法糖
Vue 3.2+ 引入了 <script setup> 语法糖,进一步简化了组合式 API 的使用:
vue
<template>
<div>
<h1>{{ title }}</h1>
<p>Count: {{ count }}</p>
<p>Double count: {{ doubleCount }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script setup>
// 自动导入常用的组合式 API
import { ref, computed, onMounted } from 'vue'
// 响应式状态 - 自动暴露给模板
const count = ref(0)
const title = ref('Vue 3 Composition API Example')
// 计算属性
const doubleCount = computed(() => count.value * 2)
// 方法
function increment() {
count.value++
}
// 生命周期钩子
onMounted(() => {
console.log('Component mounted')
})
</script>
3. 组合式 API 核心概念
3.1 响应式 API
ref
ref 是最基础的响应式 API,用于创建响应式的基本类型值(如数字、字符串、布尔值)。
typescript
// ref 的基本使用
// 设计意图:创建响应式的基本类型值,使其在模板和计算中能够自动更新
import { ref } from 'vue'
// 创建响应式状态
const count = ref(0) // 初始值为 0
const message = ref('Hello')
// 访问和修改值
console.log(count.value) // 读取值:0
count.value++ // 修改值
console.log(count.value) // 读取新值:1
// 在模板中使用(自动解包)
// <p>{{ count }}</p> // 无需 .value
reactive
reactive 用于创建响应式的对象类型值,适用于复杂的数据结构。
typescript
// reactive 的基本使用
// 设计意图:创建响应式的对象类型值,适用于包含多个属性的复杂数据结构
import { reactive } from 'vue'
// 创建响应式对象
const user = reactive({
name: 'John',
age: 30,
address: {
city: 'New York',
zip: '10001'
}
})
// 访问和修改属性
console.log(user.name) // 读取值:John
user.age++ // 修改值
console.log(user.age) // 读取新值:31
// 嵌套对象也是响应式的
user.address.city = 'Los Angeles'
console.log(user.address.city) // 读取新值:Los Angeles
// 注意:reactive 返回的是代理对象,直接替换整个对象会失去响应性
// 错误做法:user = { name: 'Jane', age: 25 } // 这样会失去响应性
computed
computed 用于创建计算属性,基于响应式状态派生新值,具有缓存特性。
typescript
// computed 的基本使用
// 设计意图:创建基于响应式状态的计算属性,自动缓存结果,只有依赖变化时才重新计算
import { ref, computed } from 'vue'
const count = ref(0)
// 创建计算属性
const doubleCount = computed(() => {
console.log('Computing doubleCount...')
return count.value * 2
})
// 首次访问会计算
console.log(doubleCount.value) // 输出: Computing doubleCount... 0
// 再次访问会使用缓存
console.log(doubleCount.value) // 输出: 0 (无计算日志)
// 依赖变化时重新计算
count.value++
console.log(doubleCount.value) // 输出: Computing doubleCount... 2
readonly
readonly 用于创建只读的响应式对象,防止修改。
typescript
// readonly 的基本使用
// 设计意图:创建只读的响应式对象,防止外部修改,适用于传递给子组件的数据
import { reactive, readonly } from 'vue'
const original = reactive({ count: 0 })
const copy = readonly(original)
// 修改原始对象会影响只读副本
original.count++
console.log(copy.count) // 输出: 1
// 尝试修改只读副本会警告
copy.count++ // 警告: Set operation on key "count" failed: target is readonly
3.2 生命周期钩子
组合式 API 提供了与选项式 API 对应的生命周期钩子,但需要从 vue 中导入使用。
typescript
// 生命周期钩子的使用
// 设计意图:在组件的不同生命周期阶段执行特定逻辑
import { onMounted, onUpdated, onUnmounted, ref } from 'vue'
const count = ref(0)
// 组件挂载后执行
onMounted(() => {
console.log('Component mounted')
// 可以在这里发起 API 请求、初始化第三方库等
})
// 组件更新后执行
onUpdated(() => {
console.log('Component updated')
// 可以在这里执行依赖于 DOM 更新的操作
})
// 组件卸载前执行
onUnmounted(() => {
console.log('Component unmounted')
// 可以在这里清理定时器、事件监听器等
})
3.3 依赖注入
provide / inject
provide 和 inject 用于组件间的依赖注入,实现跨层级组件通信。
typescript
// provide 的使用
// 设计意图:在父组件中提供数据,供子组件(无论多深)注入使用
import { provide, ref } from 'vue'
export default {
setup() {
const theme = ref('dark')
// 提供主题数据
provide('theme', theme)
return {
theme
}
}
}
// inject 的使用
// 设计意图:在子组件中注入父组件提供的数据
import { inject } from 'vue'
export default {
setup() {
// 注入主题数据
const theme = inject('theme', ref('light')) // 第二个参数是默认值
return {
theme
}
}
}
4. 组合式 API 最佳实践
4.1 逻辑复用模式
自定义 Composables
将相关逻辑封装到可复用的 composable 函数中,是组合式 API 最强大的特性之一。
typescript
// composables/useCounter.ts
// 设计意图:封装计数器逻辑,使其可在多个组件中复用
import { ref, computed } from 'vue'
/**
* 计数器逻辑
* @param initialValue 初始值
* @returns 计数器相关的状态和方法
*/
export function useCounter(initialValue = 0) {
const count = ref(initialValue)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
function decrement() {
count.value--
}
function reset() {
count.value = initialValue
}
return {
count,
doubleCount,
increment,
decrement,
reset
}
}
// 在组件中使用
import { useCounter } from '@/composables/useCounter'
export default {
setup() {
const { count, doubleCount, increment, decrement, reset } = useCounter(10)
return {
count,
doubleCount,
increment,
decrement,
reset
}
}
}
异步逻辑封装
typescript
// composables/useApi.ts
// 设计意图:封装 API 调用逻辑,处理加载状态和错误
import { ref } from 'vue'
/**
* API 调用逻辑
* @param apiFn API 调用函数
* @returns API 调用相关的状态和方法
*/
export function useApi(apiFn) {
const data = ref(null)
const loading = ref(false)
const error = ref(null)
async function execute(...args) {
loading.value = true
error.value = null
try {
data.value = await apiFn(...args)
return data.value
} catch (err) {
error.value = err
throw err
} finally {
loading.value = false
}
}
return {
data,
loading,
error,
execute
}
}
// 在组件中使用
import { useApi } from '@/composables/useApi'
import { fetchUser } from '@/api/user'
export default {
setup() {
const { data: user, loading, error, execute: fetchUser } = useApi(fetchUser)
// 组件挂载时获取用户数据
onMounted(() => {
fetchUser(1)
})
return {
user,
loading,
error,
fetchUser
}
}
}
4.2 代码组织策略
按功能组织代码
使用组合式 API,可以将相关逻辑组织在一起,提高代码的可读性和可维护性。
typescript
// 选项式 API vs 组合式 API 的代码组织对比
// 选项式 API - 逻辑分散在不同选项中
const OptionsComponent = {
data() {
return {
user: null,
posts: [],
loading: false,
error: null
}
},
computed: {
userFullName() {
return this.user ? `${this.user.firstName} ${this.user.lastName}` : ''
}
},
methods: {
async fetchUser() {
this.loading = true
try {
this.user = await fetchUser()
} catch (err) {
this.error = err
} finally {
this.loading = false
}
},
async fetchPosts() {
this.loading = true
try {
this.posts = await fetchPosts()
} catch (err) {
this.error = err
} finally {
this.loading = false
}
}
},
mounted() {
this.fetchUser()
this.fetchPosts()
}
}
// 组合式 API - 相关逻辑组织在一起
const CompositionComponent = {
setup() {
// 用户相关逻辑
const user = ref(null)
const userLoading = ref(false)
const userError = ref(null)
const userFullName = computed(() => {
return user.value ? `${user.value.firstName} ${user.value.lastName}` : ''
})
async function fetchUser() {
userLoading.value = true
try {
user.value = await fetchUserApi()
} catch (err) {
userError.value = err
} finally {
userLoading.value = false
}
}
// 帖子相关逻辑
const posts = ref([])
const postsLoading = ref(false)
const postsError = ref(null)
async function fetchPosts() {
postsLoading.value = true
try {
posts.value = await fetchPostsApi()
} catch (err) {
postsError.value = err
} finally {
postsLoading.value = false
}
}
// 生命周期
onMounted(() => {
fetchUser()
fetchPosts()
})
return {
user,
userLoading,
userError,
userFullName,
posts,
postsLoading,
postsError,
fetchUser,
fetchPosts
}
}
}
5. 高级用法
5.1 响应式工具
isRef、unref、toRef、toRefs
typescript
// 响应式工具的使用
// 设计意图:提供处理响应式数据的辅助工具,增强代码的灵活性
import { ref, reactive, isRef, unref, toRef, toRefs } from 'vue'
// isRef:检查是否为 ref 对象
const count = ref(0)
const user = reactive({ name: 'John' })
console.log(isRef(count)) // true
console.log(isRef(user)) // false
// unref:获取 ref 的值,非 ref 则直接返回
function getValue(value) {
return unref(value) // 等同于 value.value ?? value
}
console.log(getValue(count)) // 0
console.log(getValue('hello')) // hello
// toRef:为响应式对象的属性创建 ref
const nameRef = toRef(user, 'name')
nameRef.value = 'Jane' // 会更新原始对象
console.log(user.name) // Jane
// toRefs:将响应式对象的所有属性转换为 ref
const userRefs = toRefs(user)
// userRefs 是一个普通对象,包含 name、age 等属性的 ref
userRefs.name.value = 'Alice'
console.log(user.name) // Alice
watch 和 watchEffect
typescript
// 监听响应式数据的变化
// 设计意图:在响应式数据变化时执行特定逻辑
import { ref, watch, watchEffect } from 'vue'
const count = ref(0)
const user = ref({ name: 'John', age: 30 })
// 监听单个 ref
watch(count, (newValue, oldValue) => {
console.log(`Count changed from ${oldValue} to ${newValue}`)
})
// 监听对象的特定属性
watch(
() => user.value.name,
(newName, oldName) => {
console.log(`Name changed from ${oldName} to ${newName}`)
}
)
// 监听多个源
watch([count, () => user.value.age], ([newCount, newAge], [oldCount, oldAge]) => {
console.log(`Count: ${oldCount} -> ${newCount}`)
console.log(`Age: ${oldAge} -> ${newAge}`)
})
// watchEffect:自动追踪依赖
const stop = watchEffect(() => {
console.log(`Count: ${count.value}, Name: ${user.value.name}`)
})
// 修改依赖会触发 watchEffect
count.value++ // 输出: Count: 1, Name: John
user.value.name = 'Jane' // 输出: Count: 1, Name: Jane
// 停止监听
stop()
count.value++ // 不再触发
5.2 自定义指令
typescript
// 自定义指令的实现
// 设计意图:扩展 Vue 的 DOM 操作能力,实现特定的交互行为
import { ref } from 'vue'
// 注册全局指令
app.directive('focus', {
// 指令绑定到元素时
mounted(el) {
// 聚焦元素
el.focus()
}
})
// 带参数的自定义指令
app.directive('click-outside', {
mounted(el, binding) {
// 创建点击事件处理器
const handler = (event) => {
// 如果点击的不是元素本身或其子元素
if (!(el === event.target || el.contains(event.target))) {
// 执行绑定的表达式
binding.value(event)
}
}
// 存储处理器到元素
el._clickOutsideHandler = handler
// 添加事件监听器
document.addEventListener('click', handler)
},
unmounted(el) {
// 清理事件监听器
document.removeEventListener('click', el._clickOutsideHandler)
delete el._clickOutsideHandler
}
})
// 在组件中使用自定义指令
const ClickOutsideComponent = {
template: `
<div>
<input v-focus placeholder="This input will be focused">
<div v-click-outside="onClickOutside">
<p>Click outside this div to trigger event</p>
</div>
</div>
`,
setup() {
function onClickOutside(event) {
console.log('Clicked outside:', event.target)
}
return {
onClickOutside
}
}
}
5.3 组件间通信
provide/inject vs props/emits
typescript
// 组件间通信方式对比
// 1. Props/Emits - 父子组件通信
const ChildComponent = {
props: ['message'],
emits: ['update:message'],
template: `
<div>
<p>{{ message }}</p>
<button @click="$emit('update:message', 'Updated by child')">
Update Message
</button>
</div>
`
}
const ParentComponent = {
components: { ChildComponent },
data() {
return { message: 'Hello from parent' }
},
template: `
<child-component
:message="message"
@update:message="message = $event"
/>
`
}
// 2. Provide/Inject - 跨层级通信
const GrandparentComponent = {
setup() {
const sharedState = ref('Shared state from grandparent')
// 提供数据给所有子孙组件
provide('sharedState', sharedState)
return { sharedState }
},
template: `<parent-component />`
}
const DeepChildComponent = {
setup() {
// 注入数据
const sharedState = inject('sharedState')
return { sharedState }
},
template: `<p>{{ sharedState }}</p>`
}
6. 实战案例
6.1 表单处理
vue
<template>
<form @submit.prevent="handleSubmit">
<div>
<label for="name">Name:</label>
<input
id="name"
v-model="form.name"
:class="{ 'error': errors.name }"
>
<span v-if="errors.name" class="error-message">{{ errors.name }}</span>
</div>
<div>
<label for="email">Email:</label>
<input
id="email"
v-model="form.email"
:class="{ 'error': errors.email }"
>
<span v-if="errors.email" class="error-message">{{ errors.email }}</span>
</div>
<button type="submit" :disabled="loading">Submit</button>
<div v-if="loading" class="loading">Loading...</div>
</form>
</template>
<script setup>
import { ref, reactive, computed } from 'vue'
// 表单状态
const form = reactive({
name: '',
email: ''
})
// 错误信息
const errors = reactive({})
// 加载状态
const loading = ref(false)
// 验证表单
function validateForm() {
let isValid = true
// 重置错误
Object.keys(errors).forEach(key => delete errors[key])
// 验证姓名
if (!form.name.trim()) {
errors.name = 'Name is required'
isValid = false
}
// 验证邮箱
if (!form.email.trim()) {
errors.email = 'Email is required'
isValid = false
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.email)) {
errors.email = 'Please enter a valid email'
isValid = false
}
return isValid
}
// 提交表单
async function handleSubmit() {
if (!validateForm()) {
return
}
loading.value = true
try {
// 模拟 API 请求
await new Promise(resolve => setTimeout(resolve, 1000))
console.log('Form submitted:', form)
alert('Form submitted successfully!')
} catch (error) {
console.error('Submit error:', error)
alert('An error occurred, please try again.')
} finally {
loading.value = false
}
}
</script>
<style scoped>
.error {
border: 1px solid red;
}
.error-message {
color: red;
font-size: 0.8rem;
}
.loading {
color: #666;
margin-top: 10px;
}
</style>
6.2 模态框组件
vue
<template>
<Teleport to="body">
<div v-if="modelValue" class="modal-overlay" @click.self="close">
<div class="modal-content">
<div class="modal-header">
<h2>{{ title }}</h2>
<button class="close-btn" @click="close">×</button>
</div>
<div class="modal-body">
<slot></slot>
</div>
<div class="modal-footer">
<slot name="footer">
<button @click="close">Cancel</button>
<button @click="confirm" class="primary">Confirm</button>
</slot>
</div>
</div>
</div>
</Teleport>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue'
// 组件属性
const props = defineProps({
// 控制模态框显示/隐藏
modelValue: {
type: Boolean,
default: false
},
// 模态框标题
title: {
type: String,
default: 'Modal'
}
})
// 组件事件
const emit = defineEmits([
// 更新 v-model
'update:modelValue',
// 确认事件
'confirm'
])
// 关闭模态框
function close() {
emit('update:modelValue', false)
}
// 确认操作
function confirm() {
emit('confirm')
close()
}
</script>
<style scoped>
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-content {
background-color: white;
border-radius: 8px;
width: 90%;
max-width: 500px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
border-bottom: 1px solid #eee;
}
.modal-header h2 {
margin: 0;
font-size: 1.25rem;
}
.close-btn {
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
color: #666;
}
.modal-body {
padding: 1rem;
}
.modal-footer {
padding: 1rem;
border-top: 1px solid #eee;
display: flex;
justify-content: flex-end;
gap: 0.5rem;
}
button {
padding: 0.5rem 1rem;
border: 1px solid #ddd;
border-radius: 4px;
background-color: white;
cursor: pointer;
}
button.primary {
background-color: #3b82f6;
color: white;
border-color: #3b82f6;
}
</style>
<!-- 使用模态框组件 -->
<template>
<div>
<button @click="showModal = true">Open Modal</button>
<modal
v-model="showModal"
title="Confirm Action"
@confirm="handleConfirm"
>
<p>Are you sure you want to perform this action?</p>
</modal>
</div>
</template>
<script setup>
import { ref } from 'vue'
import Modal from './Modal.vue'
const showModal = ref(false)
function handleConfirm() {
console.log('Confirmed!')
// 执行确认操作
}
</script>
7. 性能优化
7.1 响应式优化
typescript
// 响应式优化技巧
// 1. 使用 shallowRef 处理大对象
// 设计意图:对于不需要深度响应的大对象,使用 shallowRef 提高性能
import { shallowRef, triggerRef } from 'vue'
// 大对象,只有引用变化时才需要响应
const largeObject = shallowRef({ /* 大对象数据 */ })
// 修改内部属性不会触发更新
largeObject.value.someProperty = 'new value'
// 需要手动触发更新
// triggerRef(largeObject)
// 2. 使用 markRaw 标记不需要响应的对象
import { markRaw } from 'vue'
// 第三方库实例不需要响应式
const chartInstance = markRaw(new Chart())
// 3. 使用 computed 的缓存特性
// 复杂计算应使用 computed 而非方法,利用缓存提高性能
const expensiveValue = computed(() => {
// 复杂计算
return heavyComputation(data.value)
})
// 4. 合理使用 watchEffect 的清理函数
watchEffect((onCleanup) => {
const timer = setInterval(() => {
console.log('Tick')
}, 1000)
// 清理函数,在依赖变化或组件卸载时执行
onCleanup(() => {
clearInterval(timer)
})
})
7.2 渲染优化
vue
<template>
<!-- 使用 v-memo 缓存渲染结果 -->
<div v-memo="[item.id, item.name]">
<!-- 当 item.id 和 item.name 不变时,此 div 的渲染结果会被缓存 -->
<h3>{{ item.name }}</h3>
<p>{{ item.description }}</p>
</div>
<!-- 使用 v-for 的 key 优化 -->
<div v-for="item in items" :key="item.id">
<!-- 使用唯一且稳定的 key -->
</div>
<!-- 条件渲染优化 -->
<div v-if="condition">
<!-- 条件为 false 时,此元素不会渲染 -->
</div>
<div v-else>
<!-- 替代内容 -->
</div>
</template>
<script setup>
import { computed } from 'vue'
// 使用 computed 避免不必要的渲染
const visibleItems = computed(() => {
return items.value.filter(item => item.visible)
})
// 避免在模板中使用复杂表达式
// 错误:<div>{{ items.filter(item => item.visible).length }}</div>
// 正确:<div>{{ visibleItems.length }}</div>
</script>
8. 总结与最佳实践
8.1 组合式 API 最佳实践
- 逻辑复用:将相关逻辑封装到 composable 函数中,提高代码复用性
- 代码组织:按功能组织代码,相关逻辑放在一起,提高可读性
- 类型安全:与 TypeScript 配合使用,提供更精确的类型推断
- 响应式选择:根据数据类型和使用场景选择合适的响应式 API(ref vs reactive)
- 性能考虑:合理使用 shallowRef、markRaw 等优化性能
- 生命周期管理:使用 onMounted、onUnmounted 等钩子管理副作用
- 清晰的依赖关系:使用 provide/inject 进行跨组件通信,避免 props drilling
- 模块化:将 composable 函数按功能模块组织,如 useAuth、useApi 等
8.2 常见问题与解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 响应式数据不更新 | 直接赋值 reactive 对象 | 使用 ref 或 toRefs,或修改对象属性而非替换对象 |
| 计算属性不更新 | 依赖项未正确追踪 | 确保计算函数中使用了响应式数据,而非普通值 |
| 内存泄漏 | 未清理副作用 | 在 onUnmounted 或 watchEffect 的清理函数中清理定时器、事件监听器等 |
| 性能问题 | 过度使用响应式或复杂计算 | 使用 shallowRef、markRaw、computed 缓存等优化手段 |
8.3 未来展望
Vue 3 的组合式 API 已经成为构建现代 Vue 应用的标准方式,它提供了:
- 更好的 TypeScript 集成:提供更精确的类型推断
- 更灵活的代码组织:按功能组织代码,提高可读性和可维护性
- 更强大的逻辑复用:通过 composable 函数实现跨组件逻辑复用
- 更好的性能:更细粒度的响应式系统和优化手段
随着 Vue 3 的不断发展,组合式 API 也在持续演进,为开发者提供更强大、更灵活的工具,使 Vue 应用的开发体验更加愉悦和高效。
9. 附录
9.1 组合式 API 与选项式 API 的对比
| 特性 | 组合式 API | 选项式 API |
|---|---|---|
| 代码组织 | 按功能组织 | 按选项组织 |
| 逻辑复用 | 通过函数复用 | 通过 mixins、extends |
| TypeScript 支持 | 更好的类型推断 | 依赖 this 类型 |
| 响应式系统 | 显式使用 ref、reactive | 隐式响应 |
| 学习曲线 | 稍高 | 较低 |
| 适用场景 | 大型复杂应用 | 中小型应用 |
9.2 常用 composable 函数命名规范
- use 前缀:表示 composable 函数,如 useAuth、useApi
- use + 功能描述:清晰表达函数的用途
- 返回对象:包含相关的状态和方法,命名清晰
- 类型定义:为 composable 函数和返回值添加 TypeScript 类型
9.3 参考资源
- Vue 3 官方文档 - 组合式 API
- Vue 3 官方文档 - 响应式系统
- Vue 3 组合式 API RFC
- Vue Mastery - Composition API
- Vue School - Composition API
通过本文的学习,你应该已经掌握了 Vue 3 组合式 API 的核心概念、使用方法和最佳实践。组合式 API 为 Vue 应用的开发提供了更灵活、更强大的工具,特别是在构建大型、复杂的应用时,其优势更加明显。
希望本文能够帮助你在实际项目中更好地应用 Vue 3 组合式 API,提高代码质量和开发效率。