Bei der Entwicklung des Management-Backends werden Sie auf jeden Fall auf viele Seiten zum Hinzufügen, Löschen, Ändern und Überprüfen stoßen, und die meisten Logiken dieser Seiten sind gleich. Dies führt zu einem zunehmenden Grad an Codekopplung Wir werden diese reproduzierbaren Seiten beschreiben und die verwendeten Daten in Hooks extrahieren, was nicht nur das Problem der Kopplung löst, sondern auch die Arbeitseffizienz verbessert.
Während der Entwicklung des Management-Backends werden Sie auf jeden Fall auf viele Seiten zum Hinzufügen, Löschen, Ändern und Überprüfen stoßen, und die meisten Logiken dieser Seiten sind dieselben, z. B. Grundfunktionen wie das Abrufen von Listen Daten-, Paging- und Filterfunktionen. Der Unterschied besteht in den dargestellten Datenelementen. Es gibt auch einige Aktionsschaltflächen. [Verwandte Empfehlungen: vuejs-Video-Tutorial, Web-Front-End-Entwicklung]
Wenn am Anfang nur 1 oder 2 Seiten vorhanden sind, kopieren die meisten Entwickler den Code der vorherigen Seite möglicherweise direkt und als Projekt Im weiteren Verlauf kann die Anzahl ähnlicher Seiten zunehmen, was direkt zu einem zunehmenden Grad der Projektcodekopplung führt.
Dies ist auch einer der Hauptgründe, warum einige wiederverwendbare Funktionen oder Komponenten aus dem Projekt extrahiert werden müssen
Nachfolgend kapseln wir eine allgemeine useList
, um sie an die meisten Hinzufügungen, Löschungen, Änderungen und Abfragen anzupassen Mit der Listenseite können Sie Aufgaben schneller und effizienter erledigen und pünktlich von der Arbeit kommen~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
中这里使用了 async/await 语法,假设请求出错、解构出错情况会走 catch 代码块,再关闭加载态
这里需要注意,传入的 listRequestFn 函数接收的参数数量和类型是否正常对应上 请根据实际情况进行调整
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; } }; }
别忘了,还有切换分页要处理
使用 watch
函数监听数据,当curPage
,pageSize
的值发生改变时调用loadData
函数获取新的数据。
export default function useList<ItemType extends Object>( listRequestFn: Function ) { // 忽略其他代码 // 监听分页数据改变 watch([curPage, pageSize], () => { loadData(curPage.value); }); }
现在实现了基本的列表数据获取
在庞大的数据列表中,数据筛选是必不可少的功能
通常,我会将筛选条件字段定义在一个ref
中,在请求时将ref
丢到请求函数即可。
在 useList 函数中,第二个参数接收一个filterOption
对象,对应列表中的筛选条件字段。
调整一下loadData
函数,在请求函数中传入filterOption
对象即可
注意,传入的 listRequestFn 函数接收的参数数量和类型是否正常对应上 请根据实际情况进行调整
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; } }; }
注意,这里 filterOption 参数类型需要的是 ref 类型,否则会丢失响应式 无法正常工作
在页面中,有一个重置的按钮,用于清空筛选条件。这个重复的动作可以交给 reset 函数处理。
通过使用 Reflect 将所有值设定为undefined
Hook
gekapselt, wodurch es einfacher und bequemer wird, dieselbe Funktion später auf anderen Seiten wiederzuverwenden. 🎜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(); }; }
useList
einen Parameter listRequestFn
empfangen, um Daten in der Liste anzufordern. 🎜🎜Definieren Sie eine list
-Variable zum Speichern des von der Netzwerkanforderung zurückgegebenen Dateninhalts. Da der Listendatentyp intern nicht direkt bestimmt werden kann, wird der Listendatentyp extern über Generics bereitgestellt. 🎜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"); } }; }
loadData
-Funktion in useList
, um die Datenerfassungsfunktion aufzurufen. Diese Funktion empfängt einen Parameter, um die angegebene Seitenzahl der Daten zu erhalten (optional, Standardwert ist). von curPage
). 🎜-Liste zu</ code> und <code>total
🎜Hier wird die async/await-Syntax verwendet, vorausgesetzt, dass die Anfrage fehlschlägt dekonstruiert Im Fehlerfall wird der Catch-Code-Block verwendet und dann der Ladezustand geschlossen 🎜
🎜Hier ist zu beachten, dass die Anzahl und Art der von listRequestFn empfangenen Parameter Die übergebenen Funktionen entsprechen einander normalerweise. Bitte passen Sie es an die tatsächliche Situation an /code>,pageSize
ändert, rufen Sie die FunktionloadData
auf, um neue Daten abzurufen. 🎜🎜Impuliert jetzt die grundlegende Listendatenerfassung🎜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 ) { // ... }Nach dem Login kopierenNach dem Login kopierenDatenfilter implementieren
🎜In einer großen Datenliste ist die Datenfilterung eine wesentliche Funktion🎜🎜 Normalerweise definiere ich den Filter Feld in einemref
, und übergeben Sie einfach denref
an die Anforderungsfunktion, wenn Sie eine Anfrage stellen. 🎜🎜In der useList-Funktion empfängt der zweite Parameter einfilterOption
-Objekt, das dem Filterbedingungsfeld in der Liste entspricht. 🎜🎜Passen Sie die FunktionloadData
an und übergeben Sie das ObjektfilterOption
in der Anforderungsfunktion. 🎜🎜Beachten Sie, dass Anzahl und Typ der von der eingehenden listRequestFn-Funktion empfangenen Parameter wie folgt sind korrekt Normale Korrespondenz Bitte entsprechend der tatsächlichen Situation anpassen🎜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 ) { // ... }Nach dem Login kopierenNach dem Login kopieren🎜Beachten Sie, dass der Parametertyp filterOption hier ein Ref-Typ sein muss, da sonst die Reaktionsfähigkeit verloren geht und nicht ordnungsgemäß funktioniert🎜Filterfeld löschen🎜Auf der Seite gibt es eine Schaltfläche zum Zurücksetzen, um die Filterbedingungen zu löschen. Diese wiederholte Aktion kann durch die Reset-Funktion verarbeitet werden. 🎜🎜Setzen Sie alle Werte mithilfe von Reflect auf undefiniert
und fordern Sie die Daten erneut an. 🎜什么是 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(); }; }Nach dem Login kopierenNach dem Login kopieren导出功能
除了对数据的查看,有些界面还需要有导出数据功能(例如导出 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"); } }; }Nach dem Login kopierenNach dem Login kopieren注意,传入的 exportRequestFn 函数接收的参数数量和类型是否正常对应上 请根据实际情况进行调整
优化
现在,整个
useList
已经满足了页面上的需求了,拥有了获取数据,筛选数据,导出数据,分页功能还有一些细节方面,在上面所有代码中的
try..catch
中的catch
代码片段并没有做任何的处理,只是简单的console.log
一下提供钩子
在
useList
新增一个 Options 对象参数,用于函数成功、失败时执行指定钩子函数与输出消息内容。定义 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 ) { // ... }Nach dem Login kopierenNach dem Login kopieren设置
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 ) { // ... }Nach dem Login kopierenNach dem Login kopieren在没有传递钩子的情况霞,推荐设置默认的失败时信息显示
优化
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" }); }Nach dem Login kopierenloadData 函数
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; } };Nach dem Login kopierenexportFile 函数
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?.(); } };Nach dem Login kopierenuseList 使用方法
<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>Nach dem Login kopieren本文
useList
的完整代码在 github.com/QC2168/snip…Das obige ist der detaillierte Inhalt vonVue3 schreibt die Listenseite so, um die Leistung zu verbessern und effizienter zu machen!. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!