Vue3的Composition API给我带来的一个很明显的变化,就是业务逻辑终于可以比较自然地从组件里抽出来了。
以前看到一个几百行的组件,经常会发现里面同时混着:
- 请求接口
- 分页
- 搜索
- Loading
- 错误处理
- 表单
- 弹窗
模板和业务逻辑搅在一起,看着就头大。
Composable可以很好地解决这个问题。
什么是Composable
简单来说,Composable就是一个封装了响应式状态和相关逻辑的函数。
比如:
export function useCounter() {
const count = ref(0)
function increment() {
count.value++
}
return {
count,
increment
}
}组件里:
const {
count,
increment
} = useCounter()这只是一个很简单的例子。
真正有价值的是把复杂的业务逻辑抽出去。
一个真实的列表场景
比如用户列表:
搜索
分页
加载
删除
刷新如果全部写在页面里:
UserPage.vue
├── search
├── pagination
├── request
├── delete
├── loading
└── error组件很容易变得臃肿。
可以抽成:
useUsers()内部负责:
const users = ref<User[]>([])
const loading = ref(false)
const page = ref(1)
const keyword = ref('')然后:
async function load() {
loading.value = true
try {
users.value = await getUsers({
page: page.value,
keyword: keyword.value
})
} finally {
loading.value = false
}
}页面只需要调用:
const {
users,
loading,
page,
keyword,
load
} = useUsers()Composable不应该变成"垃圾桶"
这是我觉得很重要的一点。
不能因为有了Composable,就把所有代码都往里塞。
比如:
useEverything.ts里面包含:
用户
订单
权限
主题
搜索
弹窗
分页这其实就是把一个大组件变成了一个大的Composable,没什么实质改善。
合理的做法是按职责拆:
useUsers()
usePagination()
useSearch()
usePermission()一个Composable最好能用一句话说清楚它负责什么。
Composable和工具函数的区别
这两者容易搞混。
工具函数:
function formatDate(date: Date) {
// ...
}它不依赖Vue的响应式系统。
而:
function useUsers() {
const users = ref([])
// ...
}它管理响应式状态,还可能依赖Vue的生命周期。
所以不是所有公共代码都该叫useXXX。
如果只是纯函数,就老老实实当工具函数。
Composable可以组合
这是Composition API很漂亮的地方。
比如:
function useUsers() {
const {
page,
pageSize
} = usePagination()
const {
keyword
} = useSearch()
// ...
return {
page,
pageSize,
keyword
}
}最终形成:
useUsers
├── usePagination
└── useSearch每一层都可以单独测试和复用。
生命周期也可以放进去
比如监听窗口大小:
export function useWindowSize() {
const width = ref(window.innerWidth)
const update = () => {
width.value = window.innerWidth
}
onMounted(() => {
window.addEventListener('resize', update)
})
onUnmounted(() => {
window.removeEventListener('resize', update)
})
return {
width
}
}页面不用关心事件监听什么时候加什么时候删。
这也是Composable很适合处理的场景。
我对Composable的使用原则
现在我一般遵循几个原则:
第一,先写清楚再抽象。
代码还没稳定的时候就急着抽,后面改起来反而麻烦。
第二,一个Composable一个主要职责。
第三,优先复用真正重复的业务逻辑。
第四,别为了"看起来高级"而写Composable。
有些只有五六行、只用一次的逻辑,留在组件里反而更直观。
最终目的
Composable的目的不是让项目多出几十个useXXX.ts文件。
真正的目的应该是:
让组件负责展示,让Composable负责可复用的业务状态和逻辑。
当一个页面越来越复杂的时候,如果能比较自然地把搜索、分页、请求、权限这些逻辑拆出来,Vue项目的可维护性会提升不少。
这也是我用Vue 3一段时间之后,越来越依赖Composition API的原因。