관리 백엔드를 개발하는 과정에서 페이지 추가, 삭제, 수정 및 확인을 많이 접하게 되며, 이러한 페이지의 논리는 대부분 동일하므로 아래에서 코드 결합 정도가 높아집니다. , 이러한 재현 가능한 페이지에 대해 설명하겠습니다. 사용된 데이터는 후크로 추출되어 결합 문제를 해결할 뿐만 아니라 작업 효율성도 향상시킵니다.
관리 백엔드를 개발하는 과정에서 페이지 추가, 삭제, 수정, 확인을 많이 접하게 되는데 목록 가져오기 등 기본 기능 등 페이지의 로직은 대부분 동일합니다. 데이터, 페이징, 필터링 기능. 차이점은 제시된 데이터 항목입니다. 몇 가지 작업 버튼도 있습니다. [관련 권장사항: vuejs 동영상 튜토리얼, 웹 프론트엔드 개발]
처음에 1~2페이지만 있을 경우 대부분의 개발자는 이전 페이지 코드를 직접 복사하여 프로젝트로 사용할 수 있습니다. 진행됨에 따라 유사한 페이지 수가 증가할 수 있으며 이는 직접적으로 프로젝트 코드 결합 정도의 증가로 이어집니다.
이것은 일부 재사용 가능한 기능이나 구성요소를 프로젝트에서 추출해야 하는 주요 이유 중 하나이기도 합니다
아래에서는 대부분의 추가, 삭제, 수정 및 쿼리에 적응하기 위해 일반 useList
를 캡슐화합니다. 목록 페이지를 사용하면 보다 빠르고 효율적으로 작업을 완료하고 정시에 퇴근할 수 있습니다~useList
,适配大多数增删改查的列表页面,让你更快更高效的完成任务,准点下班 ~
我们需要将一些通用的参数和函数抽离出来,封装成一个通用hook
,后续在其他页面复用相同功能更加简单方便。
export default function useList() { // 加载态 const loading = ref(false); // 当前页 const curPage = ref(1); // 总数量 const total = ref(0); // 分页大小 const pageSize = ref(10); }
思考一番,让useList
函数接收一个listRequestFn
参数,用于请求列表中的数据。
定义一个list
变量,用于存放网络请求回来的数据内容,由于在内部无法直接确定列表数据类型,通过泛型的方式让外部提供列表数据类型。
export default function useList<ItemType extends Object>( listRequestFn: Function ) { // 忽略其他代码 const list = ref<ItemType[]>([]); }
在useList
中创建一个loadData
函数,用于调用获取数据函数,该函数接收一个参数用于获取指定页数的数据(可选,默认为curPage
的值)。
list
和total
Vue Composition Api몇 가지 공통 매개변수와 함수를 추출하여 공통캡슐화
훅를 사용하면 나중에 다른 페이지에서 동일한 기능을 재사용하는 것이 더 쉽고 편리해집니다. <blockquote>
<h3 data-id="heading-3">목록 페이지에 대한 필수 페이징 데이터 정의</h3>
<p><pre class="brush:js;toolbar:false;">export default function useList<ItemType extends Object>(
listRequestFn: Function
) {
// 忽略其他代码
// 数据
const list = ref<ItemType[]>([]);
// 过滤数据
// 获取列表数据
const loadData = async (page = curPage.value) => {
// 设置加载中
loading.value = true;
try {
const {
data,
meta: { total: count },
} = await listRequestFn(pageSize.value, page);
list.value = data;
total.value = count;
} catch (error) {
console.log("请求出错了", "error");
} finally {
// 关闭加载中
loading.value = false;
}
};
}</pre></p>
<h3 data-id="heading-4">목록 데이터를 가져오는 방법</h3>
</blockquote>생각해보고 useList
함수는 목록의 데이터를 요청하는 데 사용되는 listRequestFn
매개변수를 받습니다. 네트워크 요청에 의해 반환된 데이터 내용을 저장하기 위해 list
변수를 정의합니다. 목록 데이터 유형은 내부적으로 직접 결정할 수 없기 때문에 목록 데이터 유형은 제네릭을 통해 외부적으로 제공됩니다. export default function useList<ItemType extends Object>( listRequestFn: Function ) { // 忽略其他代码 // 监听分页数据改变 watch([curPage, pageSize], () => { loadData(curPage.value); }); }
watch
函数监听数据,当curPage
,pageSize
的值发生改变时调用loadData
函数获取新的数据。export default function useList< ItemType extends Object, FilterOption extends Object >(listRequestFn: Function, filterOption: Ref<Object>) { const loadData = async (page = curPage.value) => { // 设置加载中 loading.value = true; try { const { data, meta: { total: count }, } = await listRequestFn(pageSize.value, page, filterOption.value); list.value = data; total.value = count; } catch (error) { console.log("请求出错了", "error"); } finally { // 关闭加载中 loading.value = false; } }; }
现在实现了基本的列表数据获取
在庞大的数据列表中,数据筛选是必不可少的功能
通常,我会将筛选条件字段定义在一个ref
中,在请求时将ref
丢到请求函数即可。
在 useList 函数中,第二个参数接收一个filterOption
对象,对应列表中的筛选条件字段。
调整一下loadData
函数,在请求函数中传入filterOption
useList
에 loadData
함수를 생성하여 데이터 수집 함수를 호출합니다. 이 함수는 지정된 수의 데이터 페이지를 얻기 위한 매개변수를 받습니다(선택 사항, 기본값은 curPage
값).
실행 프로세스
외부 함수를 호출하고 얻은 데이터를list
및total
에 할당로드 상태 닫기
여기에서는 async/await 구문이 사용됩니다. 요청 오류 또는 분해 오류가 발생하면 catch 코드 블록이 사용되며 로딩 상태가 닫힙니다
undefined
export default function useList< ItemType extends Object, FilterOption extends Object >(listRequestFn: Function, filterOption: Ref<Object>) { const reset = () => { if (!filterOption.value) return; const keys = Reflect.ownKeys(filterOption.value); filterOption.value = {} as FilterOption; keys.forEach((key) => { Reflect.set(filterOption.value!, key, undefined); }); loadData(); }; }🎜아직 페이징 전환이 남아 있으니 잊지 마세요🎜🎜
curPage
시 데이터를 모니터링하려면 watch
기능을 사용하세요. , pageSize 값이 변경되면 <code>loadData
함수를 호출하여 새로운 데이터를 얻어옵니다. 🎜export default function useList< ItemType extends Object, FilterOption extends Object >( listRequestFn: Function, filterOption: Ref<Object>, exportRequestFn?: Function ) { // 忽略其他代码 const exportFile = async () => { if (!exportRequestFn) { throw new Error("当前没有提供exportRequestFn函数"); } if (typeof exportRequestFn !== "function") { throw new Error("exportRequestFn必须是一个函数"); } try { const { data: { link }, } = await exportRequestFn(filterOption.value); window.open(link); } catch (error) { console.log("导出失败", "error"); } }; }🎜기본 목록 데이터 획득이 구현되었습니다🎜
ref
에 정의하고 요청할 때 요청 함수에 ref
를 던지기만 하면 됩니다. 🎜🎜useList 함수에서 두 번째 매개변수는 목록의 필터 조건 필드에 해당하는 filterOption
개체를 받습니다. 🎜🎜loadData
함수를 조정하고 요청 함수에 filterOption
개체를 전달합니다.🎜🎜🎜 전달된 listRequestFn 함수에서 수신한 매개변수의 개수와 유형이 제대로 대응하다
실제 상황에 따라 조정해주세요🎜🎜export interface MessageType { GET_DATA_IF_FAILED?: string; GET_DATA_IF_SUCCEED?: string; EXPORT_DATA_IF_FAILED?: string; EXPORT_DATA_IF_SUCCEED?: string; } export interface OptionsType { requestError?: () => void; requestSuccess?: () => void; message: MessageType; } export default function useList< ItemType extends Object, FilterOption extends Object >( listRequestFn: Function, filterOption: Ref<Object>, exportRequestFn?: Function, options? :OptionsType ) { // ... }🎜🎜여기의 filterOption 매개변수 유형은 ref 유형이어야 합니다. 그렇지 않으면 응답성이 손실되어 제대로 작동할 수 없습니다.🎜🎜🎜필터 필드를 지우세요🎜🎜페이지에 필터 조건을 지우는 데 사용되는 재설정 버튼입니다. 이렇게 반복되는 작업은 재설정 기능으로 처리할 수 있습니다. 🎜🎜Reflect를 사용하여 모든 값을 🎜로 설정하고 데이터를 다시 요청하세요. 🎜
什么是 Reflect?看看这一篇文章Reflect 映射对象
export default function useList< ItemType extends Object, FilterOption extends Object >(listRequestFn: Function, filterOption: Ref<Object>) { const reset = () => { if (!filterOption.value) return; const keys = Reflect.ownKeys(filterOption.value); filterOption.value = {} as FilterOption; keys.forEach((key) => { Reflect.set(filterOption.value!, key, undefined); }); loadData(); }; }
除了对数据的查看,有些界面还需要有导出数据功能(例如导出 csv,excel 文件),我们也把导出功能写到useList
里
通常,导出功能是调用后端提供的导出Api
获取一个文件下载地址,和loadData
函数类似,从外部获取exportRequestFn
函数来调用Api
在函数中,新增一个exportFile
函数调用它。
export default function useList< ItemType extends Object, FilterOption extends Object >( listRequestFn: Function, filterOption: Ref<Object>, exportRequestFn?: Function ) { // 忽略其他代码 const exportFile = async () => { if (!exportRequestFn) { throw new Error("当前没有提供exportRequestFn函数"); } if (typeof exportRequestFn !== "function") { throw new Error("exportRequestFn必须是一个函数"); } try { const { data: { link }, } = await exportRequestFn(filterOption.value); window.open(link); } catch (error) { console.log("导出失败", "error"); } }; }
注意,传入的 exportRequestFn 函数接收的参数数量和类型是否正常对应上 请根据实际情况进行调整
现在,整个useList
已经满足了页面上的需求了,拥有了获取数据,筛选数据,导出数据,分页功能
还有一些细节方面,在上面所有代码中的try..catch
中的catch
代码片段并没有做任何的处理,只是简单的console.log
一下
在useList
新增一个 Options 对象参数,用于函数成功、失败时执行指定钩子函数与输出消息内容。
export interface MessageType { GET_DATA_IF_FAILED?: string; GET_DATA_IF_SUCCEED?: string; EXPORT_DATA_IF_FAILED?: string; EXPORT_DATA_IF_SUCCEED?: string; } export interface OptionsType { requestError?: () => void; requestSuccess?: () => void; message: MessageType; } export default function useList< ItemType extends Object, FilterOption extends Object >( listRequestFn: Function, filterOption: Ref<Object>, exportRequestFn?: Function, options? :OptionsType ) { // ... }
Options
默认值const DEFAULT_MESSAGE = { GET_DATA_IF_FAILED: "获取列表数据失败", EXPORT_DATA_IF_FAILED: "导出数据失败", }; const DEFAULT_OPTIONS: OptionsType = { message: DEFAULT_MESSAGE, }; export default function useList< ItemType extends Object, FilterOption extends Object >( listRequestFn: Function, filterOption: Ref<Object>, exportRequestFn?: Function, options = DEFAULT_OPTIONS ) { // ... }
在没有传递钩子的情况霞,推荐设置默认的失败时信息显示
loadData
,exportFile
函数基于 elementui 封装 message 方法
import { ElMessage, MessageOptions } from "element-plus"; export function message(message: string, option?: MessageOptions) { ElMessage({ message, ...option }); } export function warningMessage(message: string, option?: MessageOptions) { ElMessage({ message, ...option, type: "warning" }); } export function errorMessage(message: string, option?: MessageOptions) { ElMessage({ message, ...option, type: "error" }); } export function infoMessage(message: string, option?: MessageOptions) { ElMessage({ message, ...option, type: "info" }); }
loadData 函数
const loadData = async (page = curPage.value) => { loading.value = true; try { const { data, meta: { total: count }, } = await listRequestFn(pageSize.value, page, filterOption.value); list.value = data; total.value = count; // 执行成功钩子 options?.message?.GET_DATA_IF_SUCCEED && message(options.message.GET_DATA_IF_SUCCEED); options?.requestSuccess?.(); } catch (error) { options?.message?.GET_DATA_IF_FAILED && errorMessage(options.message.GET_DATA_IF_FAILED); // 执行失败钩子 options?.requestError?.(); } finally { loading.value = false; } };
exportFile 函数
const exportFile = async () => { if (!exportRequestFn) { throw new Error("当前没有提供exportRequestFn函数"); } if (typeof exportRequestFn !== "function") { throw new Error("exportRequestFn必须是一个函数"); } try { const { data: { link }, } = await exportRequestFn(filterOption.value); window.open(link); // 显示信息 options?.message?.EXPORT_DATA_IF_SUCCEED && message(options.message.EXPORT_DATA_IF_SUCCEED); // 执行成功钩子 options?.exportSuccess?.(); } catch (error) { // 显示信息 options?.message?.EXPORT_DATA_IF_FAILED && errorMessage(options.message.EXPORT_DATA_IF_FAILED); // 执行失败钩子 options?.exportError?.(); } };
<template> <el-collapse> <el-collapse-item title="筛选条件" name="1"> <el-form label-position="left" label-width="90px" :model="filterOption"> <el-row :gutter="20"> <el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="8"> <el-form-item label="用户名"> <el-input v-model="filterOption.name" placeholder="筛选指定签名名称" /> </el-form-item> </el-col> <el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="8"> <el-form-item label="注册时间"> <el-date-picker v-model="filterOption.timeRange" type="daterange" unlink-panels range-separator="到" start-placeholder="开始时间" end-placeholder="结束时间" format="YYYY-MM-DD HH:mm" value-format="YYYY-MM-DD HH:mm" /> </el-form-item> </el-col> <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24"> <el-row class="flex mt-4"> <el-button type="primary" @click="filter">筛选</el-button> <el-button type="primary" @click="reset">重置</el-button> </el-row> </el-col> </el-row> </el-form> </el-collapse-item> </el-collapse> <el-table v-loading="loading" :data="list" border style="width: 100%"> <el-table-column label="用户名" min-width="110px"> <template #default="scope"> {{ scope.row.name }} </template> </el-table-column> <el-table-column label="手机号码" min-width="130px"> <template #default="scope"> {{ scope.row.mobile || "未绑定手机号码" }} </template> </el-table-column> <el-table-column label="邮箱地址" min-width="130px"> <template #default="scope"> {{ scope.row.email || "未绑定邮箱地址" }} </template> </el-table-column> <el-table-column prop="createAt" label="注册时间" min-width="220px" /> <el-table-column width="200px" fixed="right" label="操作"> <template #default="scope"> <el-button type="primary" link @click="detail(scope.row)" >详情</el-button > </template> </el-table-column> </el-table> <div v-if="total > 0" class="flex justify-end mt-4"> <el-pagination v-model:current-page="curPage" v-model:page-size="pageSize" background layout="sizes, prev, pager, next" :total="total" :page-sizes="[10, 30, 50]" /> </div> </template> <script setup> import { UserInfoApi } from "@/network/api/User"; import useList from "@/lib/hooks/useList/index"; const filterOption = ref<UserInfoApi.FilterOptionType>({}); const { list, loading, reset, filter, curPage, pageSize, reload, total, loadData, } = useList<UserInfoApi.UserInfo[], UserInfoApi.FilterOptionType>( UserInfoApi.list, filterOption ); </script>
本文useList
的完整代码在 github.com/QC2168/snip…
위 내용은 Vue3에서는 성능을 더 좋고 효율적으로 만들기 위해 이렇게 목록 페이지를 작성합니다!의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!