【从零到一手撕脚手架 | 第二节】模块化封装 降低耦合度 封装 axios pinia router

【从零到一手撕脚手架 | 第二节】模块化封装 降低耦合度 封装 axios pinia router

Hello大家好我是⛄,前一节我们讲解了脚手架的基础项目搭建。接下来教大家将Vue技术栈常用的工具进行封装,让我们项目的代码更易维护。

项目地址:

  • GitHub:LonelySnowman/sv3-template
  • 官方文档:SV3-Family | Vue3
  • 前置知识:Vue全家桶,了解Vite或WebPack等构建工具,Node.js
  • 您将收获到:从零到一构建一个规范的 Vue3+TS+Vite 脚手架

封装axios

状态码提示
  • 当浏览器接收并显示网页前,此网页所在的服务器会返回一个包含 HTTP 状态码的信息头(server header)用以响应浏览器的请求。
  • 每个状态码都代表一种提示信息
分类分类描述
1**信息,服务器收到请求,需要请求者继续执行操作
2**成功,操作被成功接收并处理
3**重定向,需要进一步的操作以完成请求
4**客户端错误,请求包含语法错误或无法完成请求
5**服务器错误,服务器在处理请求的过程中发生了错误

但是用户不一定了解每种状态码对应的提示信息,我们可以将状态码进行封装,将对应的中文含义返回给予用户提示。

我们封装一个方法用于获取常见状态码对应的中文信息,将他放置在 /src/utils/http/status.ts:

// 传入状态码获取对应提示信息
export const getMessage = (status: number | string): string => {let message = '';switch (status) {case 400:message = '请求错误(400)';break;case 401:message = '未授权,请重新登录(401)';break;case 403:message = '拒绝访问(403)';break;case 404:message = '请求出错(404)';break;case 408:message = '请求超时(408)';break;case 500:message = '服务器错误(500)';break;case 501:message = '服务未实现(501)';break;case 502:message = '网络错误(502)';break;case 503:message = '服务不可用(503)';break;case 504:message = '网络超时(504)';break;case 505:message = 'HTTP版本不受支持(505)';break;default:message = `连接出错(${status})!`;}return `${message},请检查网络或联系管理员!`;
};

然后我们在响应拦截器对响应码进行判断提示,如果不是成功响应发出提示给用户,这里直接使用ElementPlus的Message组件,大家可以根据习惯选择其他方式。

/src/utils/http/index.ts

import { getMessage } from './status';
import { ElMessage } from 'element-plus';// ...// axios响应拦截
// 给予用户友好提示
service.interceptors.response.use((response: AxiosResponse) => {if (response.status === 200) {return response;}ElMessage({message: getMessage(response.status),type: 'error',});return response;},// 请求失败(error: any) => {const { response } = error;if (response) {// 请求已发出,但是不在2xx的范围ElMessage({message: getMessage(response.status),type: 'error',});return Promise.reject(response.data);}ElMessage({message: '网络连接异常,请稍后再试!',type: 'error',});}
);
接口管理
  • 在我们成功将axios进项目之后,总是在组件中直接单独引用axios再进行配置请求是十分不方便的,对于一个接口我们可能会有多个组件会用到。

接口一般会有一层最外围的规范,下面以一个最简单的为例:

参数类型说明备注
codeNumber结果码成功=1失败=-1未登录=401无权限=403
messageString显示信息给予用户的提示信息
dataObject数据JSON 格式

所有接口均会按照这样的格式返回,那么我们可以使用TS设计一个类型,便于我们获得类型提示与校验。

我们将他放置在 /src/utils/http/types.ts 下,并且可以传入一个泛型进行 data 数据格式的类型校验。

export interface BaseResponse<T = any> {code: number | string;message: string;data: T;
}

这样我们就可以对第一层响应进行特殊处理,如果code不为1则说明发生错误,直接给予用户提示。

/src/utils/http/index.ts

// BaseResponse 为 res.data 的类型
// T 为 res.data.data 的类型 不同的接口会返回不同的 data 所以我们加一个泛型表示
// 此处相当于二次响应拦截
// 为响应数据进行定制化处理
const msgRequest = <T = any>(config: AxiosRequestConfig): Promise<T> => {const conf = config;return new Promise((resolve, reject) => {service.request<any, AxiosResponse<BaseResponse>>(conf).then((res: AxiosResponse<BaseResponse>) => {const data = res.data;// 如果data.code为错误代码返回message信息if (data.code != 1) {ElMessage({message: data.message,type: 'error',});reject(data.message);} else {ElMessage({message: data.message,type: 'success',});// 此处返回data信息 也就是 api 中配置好的 Response类型resolve(data.data as T);}});});
};

请求方式有多种,POST、GET、PUT、DELETE等等,为了简化axios配置项的使用,我们可以对不同的请求方式进行封装。

为了获得TS的类型校验与提示,我们传入两个泛型,一个代表请求参数类型,一个代表返回的data类型。

/src/utils/http/index.ts

// 在最后使用封装过的axios导出不同的请求方式
export function get<T = any, U = any>(config: AxiosRequestConfig,url: string,parms?: U
): Promise<T> {return msgRequest({ ...config, url, method: 'GET', params: parms });
}export function post<T = any, U = any>(config: AxiosRequestConfig,url: string,data: U
): Promise<T> {return msgRequest({ ...config, url, method: 'POST', data: data });
}
接口分类

最基础的接口封装完毕了,接下来我们要使用这些接口。就需要将这些接口分类管理,负责用户信息管理的接口放在一起,负责权限管理的接口放在一起等等。

我们在/src/api下建立不同的文件夹代表不同类型的API,在index.ts中编写接口配置,在types.ts中编写接口所需的请求参数类型以及响应类型。

/src/api/user/types.ts

// 登录所需的参数
export type LoginRequest = {username: string;password: string;
};// 刷新登录信息需要的参数
export type reLoginRequest = {accessToken: string;
};// 登录后返回的响应信息
export type LoginResponse = {username: string;roles: Array<string>;accessToken: string;
};

然后我们就可以对此类型不同的接口进行封装,之后在组件中或者再次封装在Store中使用即可。

/src/api/user/index.ts

import { post } from '@/utils/http';
// 导入类型
import { LoginRequest, LoginResponse, reLoginRequest } from '@/api/user/types';// post 请求直接传入一个 data 即可 url 我们直接在此处封装好
// 需要更改时也只需在此处更改
export const userLogin = async (data?: LoginRequest) => {return post<LoginResponse>({}, '/login', data);
};export const refreshUserInfo = async (data?: reLoginRequest) => {return post<LoginResponse>({}, '/getUserInfo', data);
};

使用的时候我们可以直接在组件中引用,也可将其封装在store的action中,将相关的store与接口关联起来

封装router

router在使用过程中如果我们直接在一个文件的一个数组中配置,最后路由越来越多会导致不易管理,我们可以将一个页面的路由配置在一个数组中最后统一导入,这样就会方便很多。

我们将不同页面的路由放置在/src/router/modules/xxx.ts

import { RouteRecordRaw } from 'vue-router';export default {path: '/login',name: 'LoginPage',component: () => import('@/views/login/index.vue'),meta: {role: ['common', 'admin'],},children: [],
} as RouteRecordRaw;

然后我们在/src/router/index.ts导入这个路由

import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
import { ElMessage } from 'element-plus';// import.meta.glob 为 vite 提供的特殊导入方式
// 它可以将模块中全部内容导入并返回一个Record对象
// 默认为懒加载模式 加入配置项 eager 取消懒加载
const modules: Record<string, any> = import.meta.glob(['./modules/*.ts'], {eager: true,
});
const routes: Array<RouteRecordRaw> = [];// 将路由全部导入数组
Object.keys(modules).forEach((key) => {routes.push(modules[key].default);
});//导入生成的路由数据
const router = createRouter({history: createWebHashHistory(),routes,
});
​
router.beforeEach(async (_to, _from, next) => {next()
});
​
router.afterEach((_to) => {NProgress.done();
});export default router;

这样我们就可以在module中直接创建路由,无需再次在index.ts中手动引入了。

封装store

同axios与touter一样,也拥有许多同类别的store数据,我们将他们放置在一个模块中便于调用,例如 user 模块专门用于保存与用户相关的信息与方法。

/src/store/index.ts

  • 这里用于导出需要使用的pinia并使用持久化插件
import { createPinia } from 'pinia';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
export default pinia;

/src/store/xxx/types.ts

  • 以 user 为例
  • 这里用于定义stroe中state数据的类型
export interface UserState {username: string;accessToken: string;refreshToken: string;roles: Array<string>;
}

/src/store/xxx/index.ts

  • 定义store模块的主要内容,state、getter、actions
  • state用于报错与用户相关的数据
  • getter保存需要二次处理的数据
  • action封装一些与user模块相关的方法,我们刚刚封装过的api如果需要直接改变用户数据直接在action中调用即可
import { defineStore } from 'pinia';
import { UserState } from './types';
import pinia from '@/store';
import { refreshUserInfo, userLogin } from '@/api/user';
import router from '@/router';export const useUserStoreHook = defineStore(// 唯一ID'User',{state: () => ({username: '游客',accessToken: '',roles: ['common'],}),getters: {},actions: {// 用于更新store数据// UserState为定义好的state类型updateInfo(partial: Partial<UserState>) {this.$patch(partial);},// 用户登录storeUserLogin(data) {return userLogin(data).then((res) => {this.username = res.username;this.roles = res.roles;this.accessToken = res.accessToken;return res;});},// 刷新用户信息refreshUserInfo() {if (this.username == '游客' && this.accessToken != '') {refreshUserInfo({accessToken: this.accessToken,}).then((res) => {this.username = res.username;this.roles = res.roles;this.accessToken = res.accessToken;}).catch(() => {this.accessToken = '';});}},},// 持久化保存 accessTokenpersist: {key: 'userInfo',storage: sessionStorage,paths: ['accessToken'],},}
);// 导出该Store
export function useUserStore() {return useUserStoreHook(pinia);
}

使用的时候我们直接在需要使用store数据的组件中引用并使用即可

<script lang='ts' setup>
import { useUserStore } from '@/store/modules/user'
userStore = useUserStore()
</script>

结语

vue3技术栈的常用的基础封装就完成了,每个人的封装习惯各不相同,只要团队用起来方便快捷就好。

一个基础的 Vue3+TypeScrpit+Vite 的项目就此构造完毕!

参考学习项目

  • fast-vue3
  • vue-pure-admin

如果有任何不正确的地方请指正,我会及时更改。

如果对你有帮助的话,请给我点个赞吧👍

关注我,后续文章不迷路⛄

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/798920.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OpenHarmony 4.1 Release版本正式发布,邀您体验

春风轻拂的4月&#xff0c;OpenAtom OpenHarmony&#xff08;以下简称“OpenHarmony”&#xff09;4.1 Release版本如期而至&#xff0c;开发套件同步升级到API 11 Release。 相比4.0 Release版本&#xff0c;4.1 Release版本应用开发的开放能力以全新的Kit维度呈现&#xff0c…

vscode 里python 工程打包成exe的方法

方法&#xff1a;auto-py-to-exe 1、安装 pip3 install auto-py-to-exe PS E:\my selfwork> pip3 install auto-py-to-exe Collecting auto-py-to-exeDownloading auto_py_to_exe-2.43.3-py2.py3-none-any.whl (187 kB)|█████████████████████████…

Node.js进阶——Express

文章目录 一、初识Express1、概念2、安装3、使用3、托管静态资源4、nodemon 二、Express路由1、概念2、使用1&#xff09;简单使用2&#xff09;模块化路由 三、Express中间件1、介绍2、语法1&#xff09;基本语法2&#xff09;next函数作用3&#xff09;定义中间件函数4&#…

K8S - Service简介和 1个简单NodePort例子

大纲图 流量方向 如上图&#xff0c; 当用户or 别的service 从k8s 集群外部访问 集群内的services 流量方向有两种 一种是垂直方向&#xff0c; 通过域名 -> Load Balancer -> gateway -> services , 在k8s 一般是通过ingress 来实现&#xff0c; 而ingress 不是本文…

基于JSP SSM的社区生活超市管理系统

目录 背景 技术简介 系统简介 界面预览 背景 随着时代步伐的加速&#xff0c;计算机技术已广泛而深刻地渗透到社会的各个层面。随着居民生活水平的持续提升&#xff0c;人们对社区生活超市的期望和管理要求也越来越高。随着社区生活超市数量的稳步增长&#xff0c;开发一个…

项目:自主实现Boost搜索引擎

文章目录 写在前面开源仓库和项目上线其他文档说明 项目背景项目的宏观原理技术栈与环境搜索引擎原理正排索引倒排索引 去标签和数据清洗模块html文件名路径保存函数html数据解析函数文件写入函数 建立索引模块检索和读取信息建立索引建立正排索引建立倒排索引jieba工具的使用倒…

mysql结构与sql执行流程

Mysql的大体结构 客户端&#xff1a;用于链接mysql的软件 连接池&#xff1a; sql接口&#xff1a; 查询解析器&#xff1a; MySQL连接层 连接层&#xff1a; 应用程序通过接口&#xff08;如odbc,jdbc&#xff09;来连接mysql&#xff0c;最先连接处理的是连接层。 连接层…

SpringCloud Alibaba Sentinel 创建流控规则

一、前言 接下来是开展一系列的 SpringCloud 的学习之旅&#xff0c;从传统的模块之间调用&#xff0c;一步步的升级为 SpringCloud 模块之间的调用&#xff0c;此篇文章为第十四篇&#xff0c;即介绍 SpringCloud Alibaba Sentinel 创建流控规则。 二、基本介绍 我们在 senti…

最新高自定义化的AI翻译(沉浸式翻译),可翻译网页和PDF等文件或者文献(附翻译API总结,Deeplx的api,Deepl的api)

前序 常见问题&#xff1a; 1.有时候想翻译网页&#xff0c;又翻译文献怎么办&#xff1f;下两个软件&#xff1f; 2.什么软件可以翻译视频字幕&#xff1f; 3.什么软件可以翻译PDF文件&#xff1f; 沉浸式翻译介绍 可以翻译文献可以翻译视频字幕可以翻译PDF文件支持OpenAI翻译…

Linux中shell脚本的学习第一天,编写脚本的规范,脚本注释、变量,特殊变量的使用等,包含面试题

4月7日没参加体侧的我自学shell的第一天 Shebang 计算机程序中&#xff0c;shebang指的是出现在文本文件的第一行前两个字符 #&#xff01; 1)以#!/bin/sh 开头的文件&#xff0c;程序在执行的时候会调用/bin/sh, 也就是bash解释器 2)以#!/usr/bin/python 开头的文件&#…

uniapp极光推送、java服务端集成

一、准备工作 1、进入【服务中心】-【开发者平台】 2、【创建应用】&#xff0c;填写应用名称和图标&#xff08;填写项目名称&#xff0c;项目logo就行&#xff0c;也可填写其他的&#xff09; 3、选择【消息推送】服务&#xff0c;点击下一步 ​ ​ Demo测试 参照文档&…

uni-app + vue3实现input输入框保留2位小数的逻辑

首先说明输入框中的格式限制如下&#xff1a; &#xff08;1&#xff09;当第一位为0时&#xff0c;第二位只能输入小数点&#xff0c;且不能输入其他数字&#xff08;如00&#xff09; &#xff08;2&#xff09;当第一位不为0时&#xff0c;后边不限制 &#xff08;3&…

Kaldi sherpa-ncnn 端侧语音识别

本文介绍一款基于新一代 Kaldi 的、超级容易安装的、实时语音识别 Python 包:sherpa-ncnn。 小编注: 它有可能是目前为止,最容易 安装的实时语音识 别 Python 包(谁试谁知道)。 它的使用方法也是极简单的。 安装 pip install sherpa-ncnn对的,就是这一句,所有的依赖都从…

论文阅读《Semantic Prompt for Few-Shot Image Recognition》

论文地址&#xff1a;https://arxiv.org/pdf/2303.14123.pdf 论文代码&#xff1a;https://github.com/WentaoChen0813/SemanticPrompt 目录 1、存在的问题2、算法简介3、算法细节3.1、预训练阶段3.2、微调阶段3.3、空间交互机制3.4、通道交互机制 4、实验4.1、对比实验4.2、组…

【C++】哈希思想的应用(位图、布隆过滤器)及海量数据处理方法

文章目录 前言位图什么是位图简单实现一个自己的位图位图的应用场景 布隆过滤器位图的缺陷及布隆过滤器的提出布隆过滤器的概念简单实现一个自己的布隆过滤器布隆过滤器的优缺点布隆过滤器的应用场景 海量数据处理 前言 哈希思想的在实际中的应用除了哈希表这个数据结构之外还…

【Redis 知识储备】读写分离/主从分离架构 -- 分布系统的演进(4)

读写分离/主从分离架构 简介出现原因架构工作原理技术案例架构优缺点 简介 将数据库读写操作分散到不同的节点上, 数据库服务器搭建主从集群, 一主一从, 一主多从都可以, 数据库主机负责写操作, 从机只负责读操作 出现原因 数据库成为瓶颈, 而互联网应用一般读多写少, 数据库…

【Pytorch学习笔记(三)】张量的运算(2)

一、引言 在《张量的运算(1)》中我们已经学习了几种张量中常用的非算数运算如张量的索引与切片&#xff0c;张量的拼接等。本节我们继续学习张量的算术运算。 二、张量的算术运算 &#xff08;一&#xff09;对应元素的加减乘除 在 PyTorch 中&#xff0c;张量的对应元素的…

C++的List类(一):List类的基本概念

目录 前言 List类的基本概念 List的构造函数 List类迭代器的使用 List的功能 List的元素访问 List与vector比较 前言 vector的insert和erase都会导致迭代器失效list的insert不会导致迭代器失效&#xff0c;erase会导致迭代器失效 insert导致失效的原因是开辟了新空间后…

massif-visualizer qpa.plugin: Could not load the Qt platform plugin “xcb“ in

massif-visualizer qpa.plugin: Could not load the Qt platform plugin "xcb" in 报这个错误&#xff0c;是因为&#xff0c;必现在 界面窗口 执行 $ massif-visualizer massif.log 如果是ssh远程链接执行&#xff0c;就会报错. #windows上查看 需要 安装远程桌…

全球化业务的网络安全挑战

随着企业业务的全球化&#xff0c;跨国数据传输和用户跨地域访问成为常态。这不仅带来了巨大的商业机会&#xff0c;也带来了以下网络安全挑战&#xff1a; 数据泄露风险&#xff1a;跨国数据传输增加了数据被截获和泄露的风险。访问限制&#xff1a;某些地区可能对互联网内容…