组合式函数(Composables)设计原则
Vue3的组合式API将逻辑关注点从选项式API的分散组织(data、methods、computed分置)转变为按功能聚合的Composables。一个设计良好的Composable应满足:单一职责、输入参数可响应式、返回值包含响应式状态和方法、支持生命周期钩子注入。
核心命名规范:Composable函数以use前缀开头,如useUserList、usePagination。返回值使用解构赋值,不要直接返回reactive对象(避免丢失响应性)。
实战:分页查询Composable封装
分页查询是后台管理系统中最常见的交互模式,封装为通用Composable后各列表页复用:
import { ref, computed, watch } from "vue"
import type { Ref } from "vue"
interface PaginationState<T> {
data: Ref<T[]>
total: Ref<number>
loading: Ref<boolean>
currentPage: Ref<number>
pageSize: Ref<number>
fetch: (params?: Record<string, unknown>) => Promise<void>
changePage: (page: number) => void
changePageSize: (size: number) => void
}
export function usePagination<T>(
fetchFn: (params: {
page: number
pageSize: number
[key: string]: unknown
}) => Promise<{ data: T[]; total: number }>,
options: { defaultPageSize?: number; immediate?: boolean } = {}
): PaginationState<T> {
const data = ref<T[]>([]) as Ref<T[]>
const total = ref(0)
const loading = ref(false)
const currentPage = ref(1)
const pageSize = ref(options.defaultPageSize ?? 10)
const fetch = async (
extraParams: Record<string, unknown> = {}
) => {
loading.value = true
try {
const result = await fetchFn({
page: currentPage.value,
pageSize: pageSize.value,
...extraParams,
})
data.value = result.data
total.value = result.total
} finally {
loading.value = false
}
}
const changePage = (page: number) => {
currentPage.value = page
}
const changePageSize = (size: number) => {
pageSize.value = size
currentPage.value = 1
}
watch([currentPage, pageSize], () => {
fetch()
})
if (options.immediate !== false) {
fetch()
}
return {
data, total, loading,
currentPage, pageSize,
fetch, changePage, changePageSize,
}
}
Composable组合:表单与校验联动
实际业务中,Composable需要与其他逻辑组合使用。以下示例展示表单提交Composable与校验Composable的组合:
export function useFormSubmit<
T extends Record<string, unknown>
>(
submitFn: (form: T) => Promise<void>,
options: { resetOnSuccess?: boolean } = {}
) {
const submitting = ref(false)
const errorMsg = ref("")
const handleSubmit = async (form: T) => {
submitting.value = true
errorMsg.value = ""
try {
await submitFn(form)
} catch (err: unknown) {
errorMsg.value = err instanceof Error
? err.message
: "提交失败"
throw err
} finally {
submitting.value = false
}
}
return { submitting, errorMsg, handleSubmit }
}
// 在组件中组合使用
const { data, fetch, loading } = usePagination(fetchUserList)
const { submitting, handleSubmit } = useFormSubmit(createUser, {
resetOnSuccess: true,
})
const onCreate = async (form: UserForm) => {
await handleSubmit(form)
fetch()
}
响应式布局中的Composable应用
在响应式布局场景下,Composable可封装断点检测逻辑,避免各组件重复监听resize事件:
import { ref, computed, onMounted, onUnmounted } from "vue"
interface Breakpoints {
isMobile: Ref<boolean>
isTablet: Ref<boolean>
isDesktop: Ref<boolean>
width: Ref<number>
}
export function useBreakpoints(): Breakpoints {
const width = ref(window.innerWidth)
const isMobile = computed(() => width.value < 768)
const isTablet = computed(() =>
width.value >= 768 && width.value < 1024
)
const isDesktop = computed(() => width.value >= 1024)
const onResize = () => {
width.value = window.innerWidth
}
onMounted(() =>
window.addEventListener("resize", onResize)
)
onUnmounted(() =>
window.removeEventListener("resize", onResize)
)
return { isMobile, isTablet, isDesktop, width }
}
前端工程化:Composable的单元测试
Composable作为纯逻辑单元,天然适合单元测试。使用@vue/test-utils的mount + setup方式:
import { usePagination } from "../composables/usePagination"
import { describe, it, expect, vi } from "vitest"
describe("usePagination", () => {
it("应在首次挂载时自动请求数据", async () => {
const mockFetch = vi.fn().mockResolvedValue({
data: [{ id: 1 }],
total: 1,
})
const { data, total, loading } = usePagination(
mockFetch, { immediate: false }
)
await mockFetch({ page: 1, pageSize: 10 })
expect(mockFetch).toHaveBeenCalled()
})
it("切换页码应触发重新查询", async () => {
const mockFetch = vi.fn().mockResolvedValue({
data: [],
total: 0,
})
const { changePage, currentPage } = usePagination(
mockFetch, { immediate: false }
)
changePage(3)
expect(currentPage.value).toBe(3)
})
})
Composable封装的核心价值在于:将业务逻辑从组件中剥离,使组件模板只负责UI渲染,逻辑层可独立测试、复用和组合。这种分层架构显著提升了大型Vue3项目的可维护性。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/vue3-zu-he-shi-api-feng-zhuang-ye-wu-luo-ji-de-zui-jia-shi/