Vue 3 Hooks:组合式API实践
·
前言:从选项式到组合式的思维转变
我们从前面开始讲ref到生命周期,今天我们来讲下vue3特有的功能hooks如果没有他就不算体验到vue3的组合式api了,代码有点多,不喜欢看代码的小伙伴可以看注释,接下来我们来看看:
想象一下,你要开发一个简单的页面:上面有一个计数器,下面可以随机展示小狗图片。在 Vue 2 时代,你可能会这样写:
//vue
<template>
<h2>当前求和为:{{ sum }}</h2>
<button @click="increment">点我+1</button>
<button @click="decrement">点我-1</button>
<hr>
<img v-for="(url, index) in dogList" :key="index" :src="url">
<button @click="getDog">再来一只狗</button>
</template>
<script>
export default {
data() {
return {
sum: 0, // 计数器数据
dogList: [] // 小狗图片列表
}
},
methods: {
increment() { // 计数器+1方法
this.sum++
},
decrement() { // 计数器-1方法
decrement() { // 计数器-1方法
this.sum--
},
async getDog() { // 获取小狗图片方法
const response = await fetch('https://dog.ceo/api/breed/pembroke/images/random')
const data = await response.json()
this.dogList.push(data.message)
}
},
mounted() {
this.getDog() // 页面加载时自动获取一张图片
}
}
</script>
这种写法看起来挺清晰的,但随着功能增加,问题就来了...
一、为什么要用 Hooks?一个现实的痛点
假如现在需求变了:
-
计数器需要添加"重置"功能
-
小狗图库需要添加加载状态和错误处理
-
还要在其他页面复用这些功能
传统的 Vue 2 写法就会变得很臃肿,而且代码无法复用!
二、Vue 3 Hooks 的优雅解决方案
2.1 创建计数器 Hook
// hooks/useSum.ts
import { ref, onMounted } from 'vue'
// 创建一个专门管理计数器逻辑的 Hook
export default function useSum() {
// 1. 定义计数器数据
let sum = ref(0)
// 2. 定义操作计数器的方法
const increment = () => {
sum.value += 1
}
const decrement = () => {
sum.value -= 1
}
// 3. 组件挂载时的初始化逻辑
onMounted(() => {
increment() // 页面加载时默认+1
})
// 4. 把数据和方法"打包"返回给组件使用
return {
sum, // 当前计数值
increment, // +1 方法
decrement // -1 方法
}
}
2.2 创建小狗图库 Hook
// hooks/useDog.ts
import { reactive } from 'vue'
import axios, { AxiosError } from 'axios'
// 创建一个专门管理小狗图片逻辑的 Hook
export default function useDog() {
// 1. 定义小狗图库的完整状态
let dogList = reactive<{
urlList: string[], // 图片URL列表
isLoading: boolean, // 加载状态
error: string | null // 错误信息
}>({
urlList: [],
isLoading: false,
error: null
})
// 2. 获取小狗图片的方法
async function getDog() {
try {
dogList.isLoading = true // 开始加载
dogList.error = null // 清空错误
// 发送请求获取随机小狗图片
let { data } = await axios.get('https://dog.ceo/api/breed/pembroke/images/random')
// 将新图片添加到列表
dogList.urlList.push(data.message)
} catch (error) {
// 错误处理
const err = error as AxiosError
dogList.error = err.message
console.log('获取小狗图片失败:', err.message)
} finally {
dogList.isLoading = false // 结束加载
}
}
// 3. 向外部暴露数据和方法
return {
dogList, // 图库状态(图片列表、加载状态、错误信息)
getDog // 获取新图片的方法
}
}
2.3 在组件中使用 Hooks
vue
<template>
<!-- 计数器区域 -->
<h2>当前求和为:{{ sum }}</h2>
<button @click="increment">点我+1</button>
<button @click="decrement">点我-1</button>
<hr>
<!-- 小狗图库区域 -->
<img v-for="(url, index) in dogList.urlList" :key="index" :src="url">
<span v-show="dogList.isLoading">加载中......</span>
<span v-show="dogList.error" style="color: red;">{{ dogList.error }}</span>
<br>
<button @click="getDog">再来一只狗</button>
</template>
<script setup lang="ts">
// 导入自定义 Hooks
import useSum from './hooks/useSum'
import useDog from './hooks/useDog'
// 使用计数器 Hook - 就像调用普通函数一样
let { sum, increment, decrement } = useSum()
// 使用小狗图库 Hook
let { dogList, getDog } = useDog()
</script>
三、Hooks 带来的巨大优势
3.1 代码复用:一次编写,到处使用
现在你可以在其他页面轻松复用这些功能:
vue
<!-- 另一个页面也想要计数器 -->
<script setup lang="ts">
import useSum from './hooks/useSum'
let { sum, increment } = useSum()
// 直接使用,无需重复编写计数器逻辑!
</script>
3.2 逻辑分离:各司其职,清晰明了
| 职责分离 | 传统写法 | Hooks 写法 |
|---|---|---|
| 计数器逻辑 | 混在组件的 data/methods 中 | 独立的 useSum Hook |
| 图库逻辑 | 混在组件的 data/methods 中 | 独立的 useDog Hook |
| 组件本身 | 既要管UI又要管逻辑 | 只关心UI和Hook的组装 |
3.3 易于维护:修改逻辑不影响组件
如果要给计数器添加"重置"功能:
// 只需修改 useSum.ts,不影响任何使用它的组件
export default function useSum() {
let sum = ref(0)
const increment = () => { sum.value += 1 }
const decrement = () => { sum.value -= 1 }
// 新增重置功能
const reset = () => {
sum.value = 0
}
return {
sum,
increment,
decrement,
reset // 暴露新方法
}
}
所有使用这个 Hook 的组件自动获得新功能!
四、Hooks 的设计原则
4.1 单一职责原则
-
useSum:只负责计数器逻辑 -
useDog:只负责小狗图库逻辑 -
组件:只负责界面渲染和 Hook 组装
4.2 高内聚低耦合
-
高内聚:相关逻辑聚集在同一个 Hook 中
-
低耦合:Hooks 之间相互独立,可以任意组合
4.3 易于测试
// 可以单独测试 Hook,不需要渲染组件
test('useSum hook', () => {
const { sum, increment } = useSum()
expect(sum.value).toBe(0)
increment()
expect(sum.value).toBe(1)
})
五、从选项式到组合式的思维转变
Vue 2 思维:"这个功能应该放在哪个选项里?"
export default {
data() { // 数据放这里
return { sum: 0, dogList: [] }
},
methods: { // 方法放这里
increment() { /* ... */ },
getDog() { /* ... */ }
},
mounted() { // 初始化放这里
this.getDog()
}
}
Vue 3 思维:"这个功能如何封装成 Hook?"
// 计数器功能封装
export function useSum() {
const sum = ref(0)
const increment = () => sum.value++
return { sum, increment }
}
// 图库功能封装
export function useDog() {
const dogList = ref([])
const getDog = async () => { /* ... */ }
return { dogList, getDog }
}
// 组件中组合使用
const { sum, increment } = useSum()
const { dogList, getDog } = useDog()
六、总结:为什么 Hooks 是 Vue 3 的精髓
通过这个简单的计数器和小狗图库例子,你应该能体会到:
-
更好的代码组织:相关逻辑聚集,不再碎片化
-
极强的复用性:一次封装,无限复用
-
易于维护:修改逻辑只需改动一个地方
-
便于测试:可以单独测试业务逻辑
-
清晰的责任划分:UI 与逻辑彻底分离
Hooks 让 Vue 3 从"如何组织代码"的框架,变成了"如何设计功能"的框架。 这才是组合式 API 真正的威力所在!现在,试着把你项目中的一些功能抽成 Hooks 吧,你会发现代码变得前所未有的清晰和灵活!
更多推荐
所有评论(0)