首页IT科技vue3怎么写组件(记录–在Vue3这样子写页面更快更高效)

vue3怎么写组件(记录–在Vue3这样子写页面更快更高效)

时间2025-04-30 15:08:47分类IT科技浏览4392
导读:这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助...

这里给大家分享我在网上总结出来的一些知识             ,希望对大家有所帮助

前言

在开发管理后台过程中                  ,一定会遇到不少了增删改查页面      ,而这些页面的逻辑大多都是相同的             ,如获取列表数据                   ,分页      ,筛选功能这些基本功能             。而不同的是呈现出来的数据项                  。还有一些操作按钮      。

如何获取列表数据

思考一番      ,让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             ,再重新请求一次数据            。

什么是 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-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> <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 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>

本文useList的完整代码在github.com/QC2168/snip…

本文转载于:

https://juejin.cn/post/7172889961446768670

如果对您有所帮助             ,欢迎您点个关注                   ,我会定时更新技术文档      ,大家一起讨论学习      ,一起进步                  。

声明:本站所有文章                   ,如无特殊说明或标注            ,均为本站原创发布。任何个人或组织      ,在未征得本站同意时                   ,禁止复制      、盗用             、采集                   、发布本站内容到任何网站      、书籍等各类媒体平台             。如若本站内容侵犯了原著者的合法权益            ,可联系我们进行处理                  。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
programming skills(Programming tutorials and source code examples) vue页面优化主要从哪些方面进行(Vue.js 3.x 优化概览)