前言
在后台管理系统日常开发中分页表格是比较多的场景,这些开发场景都大同小异,但在每一个页面都重复实现是比较boring的(浪费时间拧螺丝🤐)。在Vue2中你可能会是使用mixins去实现这一套交互,包括数据、搜索、分页加载等,但是使用mixins存在命名冲突问题,并且随着业务堆叠会对项目维护带来巨大的问题。
随着Vue3的问世,hook的运用也在vue中使用起来,详情戳文档#组合式函数, 虽然篇幅没有涉及hook,vue3官方文档也没有定义hooks,但却处处都是hooks😁
什么是hooks
其实在行业里面没有一个标准的明确的定义,在我看来就是具有满足一类业务场景的可复用方法(类似于utils),hooks与mixins、utils区别:
- hooks和utils的最大区别就是hooks是特定的业务场景的衍生物,而utils更偏向通用工具。
- hooks其实和mixins最大的区别在思想上,vue hooks是利用Vue的组合式 API (Composition API)来封装和复用有状态逻辑的函数、耦合度更低。而mixins是基于potions Api,使用自由度更低、存在命名冲突,这一点也让维护难度增大。
尝试封装
- 状态声明:表格列表数据、查询参数、页码、页面等等
- 基本交互方法,包括切换页码、每页条数、查询、数据拉取、重置标调
- 支持立即执行、数据格式化 通过options控制
- 支持初始化查询参数扩展 通过initParam传入
js
import { reactive, computed, toRefs, onBeforeMount } from "vue";
/**
* @description table 页面表格操作方法封装
* @param {Function} request 请求方法 这里我使用的自己基于axios封装的类似await-to-js库
* @param {Object} initParam 获取数据初始化参数(非必传,默认为{})
* @param {Boolean} isPageAble 是否有分页(非必传,默认为true)
* @param {Boolean} immediate 是否立即触发 (非必传,默认为true)
* @param {Function} dataCallBack 对后台返回的数据进行处理的方法(非必传)
* @param {Number} minPageShowCount 最小显示页码选择器条数
* */
export function useTable ({ request, initParam, isPageAble = true, immediate = true, dataCallBack, minPageShowCount = 10 }) {
//初始页面
const INITIAL_PAGE_PARAMS = {
// 当前页数
current: 1,
// 每页显示条数
size: 10,
// 总条数
total: 0
}
const state = reactive({
tableData: [],
//分页数据
pagination: {
...INITIAL_PAGE_PARAMS
},
// 查询参数(只包括查询)
searchParam: {},
// 初始化默认的查询参数
searchInitParam: {},
// 总参数(包含分页和查询参数)
totalParam: {},
// 是否加载中
loading: false,
// 接口返回的所有内容
responseData: null
})
//这里传入后台需要的页码数据 字段名自行定义即可
const pageParam = computed(() => ({
page: state.pagination.current,
pageSize: state.pagination.size
}))
//是否展示分页器
const isPaginationVisible = computed(() => {
return isPageAble && state.pagination.total > minPageShowCount
})
//获取数据
const getTableData = async() => {
Object.assign(state.totalParam, isPageAble ? pageParam.value : {}, initParam)
initParam && (state.searchInitParam = initParam)
state.loading = true;
const [error, res] = await request(state.totalParam)
if(error) {
//这里根据业务需求做判断
xxx.error(error.msg)
return
}
state.tableData = res.data.list;
state.responseData = res
try {
dataCallBack && (state.tableData = dataCallBack(res));
} catch (error) {
console.error(error,'Format error')
Message.error('格式化数据错误')
}
state.loading = false;
isPageAble && updatePagination({ total: 数据总数 });
}
if(immediate) onBeforeMount(async () => {
await getTableData()
})
const updatePagination = (pagination) => {
Object.assign(state.pagination, pagination);
};
const updatedTotalParam = () => {
state.totalParam = {};
// 处理查询参数,可以给查询参数加自定义前缀操作
let currentSearchParam = {};
// 防止手动清空输入框携带参数(这里可以自定义查询参数前缀)
for (let key in state.searchParam) {
// * 某些情况下参数为 false/0 也应该携带参数
if (Reflect.has(state.searchParam, key)) {
currentSearchParam[key] = state.searchParam[key];
}
}
Object.assign(state.totalParam, currentSearchParam, isPageAble ? pageParam.value : {});
};
const resetParams = () => {
state.searchParam = state.searchInitParam;
state.pagination = INITIAL_PAGE_PARAMS;
Object.assign(state.totalParam, state.searchParam, isPageAble ? pageParam.value : {});
}
const refreshTable = () => {
resetParams()
getTableData()
}
const search = () => {
state.pagination.current = 1;
updatedTotalParam();
getTableData();
};
const onSizeChange = (val) => {
state.pagination.current = 1;
state.pagination.pageSize = val;
getTableData();
};
const onPageChange = (val) => {
state.pagination.current = val;
getTableData();
};
return {
...toRefs(state),
isPaginationVisible,
refreshTable,
search,
onSizeChange,
onPageChange,
getTableData
}
}
使用方法
只要定义好参数传入即可,使用非常的简便,不需要业务重复实现,有特殊场景业务自行扩充即可
js
const {
loading,
tableData,
pagination,
search,
onSizeChange,
searchParam,
onPageChange,
isPaginationVisible
} = useTable({
request,
dataCallback,
initParam: { }
})
总结
随着vue3支持使用hooks,使用hooks进行开发可以减少重复性的工作,我们日常开发也越来越快(造轮子的好处😁)。目前vue也有一套比较成熟的hooks库VueUse,感兴趣的可以前往了解。