Vue3 的 Composition API 為我們提供了另一種代碼組織方式,這個概念借鑒自 React 的 Hook。在 16.8 的版本中,React 引入了 React Hook,通過封裝有狀态的函數,提高了元件的編寫效率和可維護性,在後面統一使用 Hook 來替代“組合式函數”。
Hook 允許我們将邏輯封裝為可複用的函數,這些函數可以讓你在元件之外管理狀态和邏輯,進而在多個元件之間共享和複用。
封裝自己的 Vue Hook
下面我們通過一個實際的業務場景來學習如何封裝自己的 Vue Hook。我們将建立一個 useTable Hook,它用于從 API 擷取表格資料并支援分頁。
基礎版useTableHook
首先,我們定義一個簡單的 useTable Hook,它通過調用 API 傳回表格資料,并支援重新整理。
// useTable.js
import { ref } from 'vue';
export function useTable(api) {
const data = ref([]);
const refresh = () => {
api().then(res => data.value = res);
};
refresh();
return [data, refresh];
}
支援分頁查詢
為了使 useTable 支援分頁查詢,我們需要對 API 進行改造,使其能夠接受分頁參數并傳回分頁資料。
// api.ts
export const getTableDataApi = (page, limit) => {
// ... 接口傳回分頁資料
}
然後,我們更新 useTable Hook 以處理分頁邏輯。
// const sizeOption = [10, 20, 50, 100, 200];
// useTable.js
import { reactive } from 'vue';
export function useTable(api, options) {
const pagination = reactive({
current: 1,
total: 0,
sizeOption,
size: sizeOption[0],
// ... 分頁方法
});
// ... 重新整理資料的邏輯
return [pagination, refresh];
}
處理 API 參數和加載狀态
為了使 useTable 更加靈活,我們可以讓 API 接受參數,并添加加載狀态的管理。
// useTable.js
import { get } from "lodash-es";
export function useTable(api, options) {
const { path = {}, immediate = false } = options || {};
const { data: dataPath, total: totalPath, page: pagePath, size: sizePath } = path;
const [pagination, setPagination] = useState({
current: 1,
size: 10,
total: 0,
});
const [loading, setLoading] = useState(false);
const data = ref([]);
const refresh = async () => {
setLoading(true);
const { current, size } = pagination;
const res = await api({ [pagePath]: current, [sizePath]: size });
data.value = get(res, dataPath) || [];
setPagination(prevState => ({ ...prevState, total: get(res, totalPath) || 0 }));
setLoading(false);
};
useEffect(() => {
if (immediate) {
refresh();
}
}, [immediate]);
return { data, pagination, refresh, loading };
}
在這個版本中,useTable Hook 接受一個 API 函數和一個選項對象,其中選項對象可以包含路徑資訊和是否立即重新整理資料的标志。我們還添加了 loading 狀态。
使用useTableHook
在元件中,我們可以通過 useTable Hook 的傳回值來控制分頁和資料加載。
<script setup>
import { useTable } from './useTable.js';
import { getTableDataApi } from './api.ts';
const { data, pagination, refresh, loading } = useTable(getTableDataApi, {
path: {
data: 'items',
total: 'totalCount',
page: 'currentPage',
size: 'pageSize'
},
immediate: true
});
</script>
<template>
<el-table :data="data" style="width: 100%">
<!-- 列定義 -->
</el-table>
<el-pagination
v-model:current-page="pagination.current"
:page-size="pagination.size"
:total="pagination.total"
></el-pagination>
<button @click="refresh">Refresh</button>
<div v-if="loading">Loading...</div>
</template>
在這個例子中,我們使用 Element Plus 的表格和分頁元件來展示 useTable Hook 的功能。我們可以通過分頁元件來改變目前頁和每頁大小,并使用 refresh 函數來手動重新整理資料。
總結
我們學習了如何封裝一個功能完善的 useTable Hook,它不僅支援分頁查詢,還能夠處理 API 參數和加載狀态。這樣的封裝使得我們的代碼可維護更高,同時也提高了開發效率。