在Vue3这样子写页面更快更高效
前言
在开发管理后台过程中,一定会遇到不少了增删改查页面,而这些页面的逻辑大多都是相同的,如获取列表数据,分页,筛选功能这些基本功能。而不同的是呈现出来的数据项。还有一些操作按钮。
对于刚开始只有 1,2 个页面的时候大多数开发者可能会直接将之前的页面代码再拷贝多一份出来,而随着项目的推进类似页面数量可能会越来越多,这直接导致项目代码耦合度越来越高。
这也是为什么在项目中一些可复用的函数或组件要抽离出来的主要原因之一
下面,我们封装一个通用的useList
,适配大多数增删改查的列表页面,让你更快更高效的完成任务,准点下班 ~
前置知识
- Vue
- Vue Composition Api
封装
我们需要将一些通用的参数和函数抽离出来,封装成一个通用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
的值)。
- 执行流程
1.设置加载状态
2.调用外部传入的函数,将获取到的数据赋值到list
和total
中
3.关闭加载态
这里使用了 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
,再重新请求一次数据。
什么是 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 对象参数,用于函数成功、失败时执行指定钩子函数与输出消息内容。
定义 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?.();}
};
useList 使用方法
<template><el-collapse class="mb-6"><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-inputv-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-pickerv-model="filterOption.timeRange"type="daterange"unlink-panelsrange-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-paginationv-model:current-page="curPage"v-model:page-size="pageSize"backgroundlayout="sizes, prev, pager, next":total="total":page-sizes="[10, 30, 50]"/></div>
</template>
<script setup lang="ts">
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>
最后
整理了一套《前端大厂面试宝典》,包含了HTML、CSS、JavaScript、HTTP、TCP协议、浏览器、VUE、React、数据结构和算法,一共201道面试题,并对每个问题作出了回答和解析。
有需要的小伙伴,可以点击文末卡片领取这份文档,无偿分享
部分文档展示:
文章篇幅有限,后面的内容就不一一展示了
有需要的小伙伴,可以点下方卡片免费领取
相关文章:
在Vue3这样子写页面更快更高效
前言 在开发管理后台过程中,一定会遇到不少了增删改查页面,而这些页面的逻辑大多都是相同的,如获取列表数据,分页,筛选功能这些基本功能。而不同的是呈现出来的数据项。还有一些操作按钮。 对于刚开始只有 1ÿ…...
做软件测试,如何才能实现月入20K?
听我的,测试想要月入20k。 首先你要去大厂,不在大厂起码也得在一线城市,北上广深。 二线城市的话成都、杭州最好。 不然的话想都不要想。 像我之前整理过成都的公司,除了字节跳动、蚂蚁金服、滴滴、美团、京东、平安、字节跳动…...
mysql last lesson
1:创建用户 create user zhang identified by 12345678;2:给用户授权,撤销授权, grant.......to revoke ....... 3:将数据库中的数据导出 C:\Windows\system32>mysqldump bjpowernode>C:\bjpowernode.sql -uroot -p12345678 4&#…...
一、Redis入门概述(是什么,能干嘛,去哪下,怎么玩)
一. redis是什么? Redis:REmote Dictionary Server(远程字典服务器)官方解释: Remote Dictionary Server(远程字典服务)是完全开源的,使用ANSIC语言编写遵守BSD协议,是一个高性能的Key-Value数据库提供了丰富的数据结构ÿ…...
(六十二)当我们在SQL里进行分组的时候,如何才能使用索引?
今天我们接着上次的内容来谈谈在SQL语句里假设你要是用到了group by分组语句的话是否可以用上索引,因为大家都知道,有时候我们会想要做一个group by把数据分组接着用count sum之类的聚合函数做一个聚合统计。 那假设你要是走一个类似select count(*) fr…...
python字符串练习
python字符串练习 1.去掉字符串中所有的空格 s This is a demo print(s.replace( , )) 2.获取字符串中数字的个数 data input("请输入一些字符串:") a 0 for i in data:if i.isdigit():a a 1 print("数字个数:", a)3.将字母全部转换为…...
Java-封装、继承、多态
封装 访问控制权限又成为“封装”,是面向对象三大特征中的一种。核心是,只对需要的类可见。 继承 继承是所有OOP(Object Oriented Programming)语言和Java语言都不可或缺的一部分。 只要创建一个类,就隐式继承自Obje…...
问题三十二:离散二维傅立叶变换(Discrete Fourier Transformation)
为了将灰度图像表示为频谱图,我们需要进行以下步骤: 加载图像并将其转换为灰度图像。对图像进行二维离散傅里叶变换。将变换结果表示为幅度谱和相位谱。可以对幅度谱和相位谱进行可视化,以查看频率分布。对幅度谱和相位谱进行逆变换…...
恢复谷歌翻译的究极方法
谷歌翻译为什么会失效,我想各位在去年11月的时候就知道了。可是要怎么解决失效的问题呢?之前我们是通过手动Ping可以连接的ip各位可能觉得麻烦,心里觉得什么档次还要我手动ping就没有可以自动扫描的吗?还别说真的有我最近发现一个…...
string函数以及string常用接口
本文介绍的是C关键字string中一些重要用法,以及各种字符串序列的处理操作 ——飘飘何所似,天地一沙鸥 文章目录前言一、string(字符串类)二、string类对象的容量操作2.1 size/length2.2 capacity2.3 empty/clear2.4 resize/reser…...
分享一篇由C语言实现《数据结构》无头无循环单链表
三月,你好,各位csdn uu们好 文章目录前言一、何为单链表二、单链表基本操作(增,删,查,改,销毁,遍历)1.查找与修改、销毁与遍历2.链表插入与删除操作三、单链表 VS 顺序表…...
C盘爆满?两个超简单的解决办法
我们在使用电脑的过程中,经常容易出现C盘爆红,反而其他盘还有大量可用空间的情况。为什么会这样呢?其实主要就两种原因:一是电脑使用习惯不好,不管什么软件都默认安装在C盘,大文件又喜欢放在桌面࿰…...
ThreadLocal
ThreadLocalThreadLocalMapgetsetremove内存泄漏key用强/弱引用entry继承了弱引用ThreadLocal 一个对象的所有线程会共享其全局变量——>线程不安全 解决方式: 方式一:同步机制,加锁(时间换空间) 方式二:…...
Java基础:JDK7-时间Date
JDK7以前时间相关类 1.Date Date date new Date(); , sout(date)得到的是现在所处位置的时间 Date date new Date(0L); , sout(date)得到的是时间原点也就是1970年1月1日08:00(东八区). date.setTime(1000L); sout(date)得到的是时间原点后一秒钟的时间 long time date.g…...
什么是IP地址?
IP协议中还有一个非常重要的内容,那就是给因特网上的每台计算机和其它设备都规定了一种地址,叫做“IP 地址”。由于有这种地址,才保证了用户在连网的计算机上操作时,能够高效而且方便地从千千万万台计算机中选出自己所需的对象来。…...
4年经验之谈,什么是接口测试?怎样做接口测试?
一、什么是接口?【文末学习资源分享】赶紧嫖!冲!!!! 接口测试主要用于外部系统与系统之间以及内部各个子系统之间的交互点,定义特定的交互点,然后通过这些交互点来,通过…...
普通指针扫盲
一、什么是指针 C语言里,变量存放在内存中,而内存其实就是一组有序字节组成的数组,每个字节有唯一的内存地址。 CPU 通过内存寻址对存储在内存中的某个指定数据对象的地址进行定位。这里,数据对象是指存储在内存 中的一个指定数据…...
深度学习笔记:神经网络权重确定初始值方法
神经网络权重不可为相同的值,比如都为0,因为如果这样网络正向传播输出和反向传播结果对于各权重都完全一样,导致设置多个权重和设一个权重毫无区别。我们需要使用随机数作为网络权重 实验程序 在以下实验中,我们使用5层神经网络…...
关于 python 的异常使用说明 (python 的文件和异常)
文章目录异常1. 处理异常 ZeroDivisionError 异常2. 使用 try-except 代码块3. 使用异常避免崩溃4. else 代码块5. 处理 FileNotFoundError 异常6. 分析文本7. 失败时一声不吭异常 pyhong 使用被异常成为异常的特殊对象来管理程序执行期间发生的错误。 每当发生让 python 不知所…...
Spark RDD持久化
RDD Cache缓存 RDD通过Cache或者Persist方法将前面的计算结果缓存,默认情况下会把数据以序列化的形式缓存在JVM的堆内存中。但是并不是这两个方法被调用时立即缓存,而是触发后面的action时,该RDD将会被缓存在计算节点的内存中,并供…...
【Linux】Linux系统安装Python3和pip3
1.说明 一般来说Linux会自带Python环境,可能是Python3或者Python2,可能有pip也可能没有pip,所以有时候需要自己安装指定的Python版本。Linux系统下的安装方式都大同小异,基本上都是下载安装包然后编译一下,再创建好软…...
用java进行base64加密
首先定义一组密钥,加密和解密使用同一组密钥private final String key "hahahahahaha";也可以随机生成密钥/*** 生成随机密钥* param keySize 密钥大小推荐128 256* return* throws NoSuchAlgorithmException*/public static String generateSecret(int keySize) th…...
torch函数合集
torch.tensor() 原型:torch.tensor(data, dtypeNone, deviceNone, requires_gradFalse) 功能:其中data可以是:list,tuple,NumPy,ndarray等其他类型,torch.tensor会从data中的数据部分做拷贝(而不是直接引用),根据原始数据类型生成相应类型的torch.Tenso…...
AcWing算法提高课-3.1.2信使
宣传一下算法提高课整理 <— CSDN个人主页:更好的阅读体验 <— 题目传送门点这里 题目描述 战争时期,前线有 nnn 个哨所,每个哨所可能会与其他若干个哨所之间有通信联系。 信使负责在哨所之间传递信息,当然,…...
Paddle OCR Win 11下的安装和简单使用教程
Paddle OCR Win 11下的安装和简单使用教程 对于中文的识别,可以考虑直接使用Paddle OCR,识别准确率和部署都相对比较方便。 环境搭建 目前PaddlePaddle 发布到v2.4,先下载paddlepaddle,再下载paddleocr。根据自己设备操作系统进…...
杂谈:数组index问题和对象key问题
面试题一: var arr [1, 2, 3, 4] 问:arr[1] ?; arr[1] ?答:arr[1] 2; arr[1] 2 这里可以再分为两个问题: 1、数组赋值 var arr [1, 2, 3, 4]arr[1] 10; // 数字场景 arr[10] 1; // 字符串场景 arr[a] 1; // 字符串…...
三天Golang快速入门—Slice切片
三天Golang快速入门—Slice切片Slice切片切片原理切片遍历append函数操作切片append添加append追加多个切片中删除元素切片合并string和slice的联系Slice切片 切片原理 由三个部分构成,指针、长度、容量指针:指向slice第一个元素对应的数组元素的地址长…...
腾讯会议演示者视图/演讲者视图
前言 使用腾讯会议共享PPT时,腾讯会议支持共享用户使用演示者视图/演讲者视图,而会议其他成员可以看到正常的放映视图。下面以Win10系统和Office为例,介绍使用步骤。值得一提的是,该方法同时适用于单显示屏和多显示屏。 腾讯会议…...
【C++】类与对象(一)
文章目录1、面向过程和面向对象初步认识2、类的引入3、类的定义4、类的访问限定符5、类的作用域6、类的实例化7、计算类对象的大小8、this指针9、 C语言和C实现Stack的对比1、面向过程和面向对象初步认识 C语言是面向过程的,关注的是过程,分析出求解问题…...
JavaScript基本语法
本文提到的绝大多数语法都是与Java不同的语法,相同的就不会赘述了.JavaScript的三种引入方式内部js<body><script>alert(hello);</script> </body>行内js<body><div onclick"alert(hello)">这是一个div 点击一下试试</div>…...
建手机号码的网站/网站权重优化
Batch、Epoch和IterationBatch(批次)Epoch(轮次)Iteration(迭代)在深度学习中,Batch、Epoch和Iteration是非常重要的概念,它们是训练模型时的三个基本单位。以下是它们的概念、区别和…...
一个做二维码问卷调查的网站/百度竞价排名怎么做
本文主要解决openssl升级到1.1.0以上版本,导致shadows2.8.2启动报undefined symbol: EVP_CIPHER_CTX_cleanup错误。 如果在安装完Shadows后,启动时报 AttributeError: /usr/local/ssl/lib/libcrypto.so.1.1: undefined symbol: EVP_CIPHER_CTX_cleanup…...
网站开发建设的步骤/免费优化网站
小班水墨画教案《小猴吃桃》适用于小班的水墨画主题教学活动当中,让幼儿能用宣纸大胆作画,掌握画水墨画小猴的基本方法,培养幼儿对国画的兴趣,快来看看幼儿园小班水墨画《小猴吃桃》教案吧。活动目标:1、培养幼儿对国画…...
江苏嘉隆工程建设有限公司网站/如何查询关键词的搜索量
修辞目的题是托福阅读中较为常见的题型之一。而这个题型比较特殊的一点就在于既需要考生结合题目中的关键词返回原文进行定位,也需要在找到对应内容后进行一些自主思考分析才能获得正确解答。如何做好这个题型?下面广州新航道小编就通过2个实例为大家讲解应对技巧。…...
建设报名系统官方网站/seo网站推广什么意思
IoC,直观地讲,就是容器控制程序之间的关系,而非传统实现中,由程序代码直接操控。这也就是所谓“控制反转”的概念所在。控制权由应用代码中转到了外部容器,控制权的转移是所谓反转。IoC还有另外一个名字——“依赖注入…...
龙江做网站/网站都有哪些
作者:张楷露、张琪 封面:自己想吧一、基本思想的异同共同点从二者表达的含义上看,主成分分析法和因子分析法都寻求少数的几个变量(或因子)来综合反映全部变量(或因子)的大部分信息,变量虽然较原始变量少,但所包含的信…...