从零到一搭建微前端项目模板
从零到一搭建微前端项目模板
微前端项目模板:Sewar-x/admin-scaffolds: 基于 vue3、typescript、element-plus、xw-ui、micro-app 的后台管理系统模板 (github.com)
admin-scaffolds
基于 vue3 + typescript + element-plus + xw-ui 的后台管理系统的脚手架
功能
功能 | 描述 |
---|---|
项目规范 | JS规范 、Git 提交规范 、样式规范 、代码格式 、Git检查 |
环境 | 开发、测试、正式、mock |
样式管理 | 全局样式、原子化 CSS、样式隔离 |
路由管理 | 顶部菜单栏、侧边栏 |
登录 | 登录、单点登录、token 校验、token 刷新 |
权限管理 | 菜单权限、按钮/组件权限 |
组件库 | JSON Scheme 公共组件库、业务组件 |
插件管理 | 按需引入、自定义插件 |
国际化 | 翻译参数查找翻译语言、本地语言数据、远程语言数据 |
主题管理 | |
开发 | mock数据、页面模板生成 |
测试 | |
构建 | 自定义构建 |
部署 | GitLab 流水线、自定义脚本 |
技术栈
功能 | 描述 | 技术 |
---|---|---|
项目规范 | JS规范 | Eslint |
Git 提交规范 | commitlint | |
样式规范 | stylelint | |
代码格式 | prettier | |
Git检查 | husky | |
环境 | 开发、测试、正式 | |
mock | vite-plugin-mock | |
样式管理 | 全局样式 | less、postcss |
原子化 CSS | unocss | |
样式隔离 | 公共样式前缀 | |
路由管理 | 顶部菜单栏、侧边栏 | Vue-Router |
权限管理 | 菜单权限、按钮/组件权限 | |
组件库 | JSON Scheme 公共组件库 | xw-ui |
组件库 | element-plus | |
插件管理 | 按需引入、自定义插件 | |
国际化 | 本地语言数据、远程语言数据 | vue-i18n |
主题管理 | ||
http | 集成全局 Token、Token刷新、校验、清除 | xw-ui/xhttp |
开发 | mock数据 | |
页面模板生成 | plop | |
测试 | ||
构建 | 自定义构建 | Vite |
部署 | GitLab 流水线、自定义脚本 |
项目配置
插件
插件为 Vue.js 应用程序提供了额外的功能或改进。
插件使用 vue 的插件机制添加到 Vue.js 中。
插件通过统一文件夹 src/plugins/
管理,管理规范:
- 所有插件放入文件夹
src/plugins/
; - 以文件夹名称命名插件;
- 插件使用 Vue.js 的插件机制添加到项目中;
- 在文件
src/plugins/init
提供插件的统一初始化方法,插件初始化方法在src/main.ts
文件中使用。
HTTP 插件
HTTP 插件是使用 XHTTP | XW-UI 插件,该插件是对 Axios 进行二次封装,增加了 登录 Token 管理相关逻辑。
Permission 插件
权限控制插件是使用 permission 插件 | XW-UI 插件,permission 插件 | XW-UI 插件是一个基于后台管理系统中的路由菜单权限控制系统,通过 vue-router 全局控制后台管理系统的菜单权限。
插件功能:
功能 | 介绍 |
---|---|
菜单路由权限控制 | 通过接口返回权限路由名称,控制当前登录用户的路由权限 |
按钮级别权限控制 | 通过接口返回按钮权限列表名称,控制当前登录用户的按钮权限 |
单点登录 | 使用当前插件的系统和其他系统相互登录 |
插件配置:
在目录 src/plugins
目录下新增 /xw-permission
目录,存在权限系统相关插件,创建插件初始化方法:
import type { App } from "vue";
import initPermission from "xw-ui/permission"
import asyncRoutes from "@/router/asyncRoutes";
import basicRoutes from "@/router/basicRoutes";
import whiteList from "@/router/basicRoutes/whiteList";
import { checkSSOLogin, getAuthList } from "@/api/login"
import requestSetting from "@/settings/requestSetting"
const publicPath = import.meta.env.VITE_BASE_PATH // 系统 publicPath 目录
export async function setupXWPermission(app: App, router: any) {
//定义一个符合 permissionOptions 接口的对象
const options = {
router,
publicPath, // 系统 publicPath 目录
whiteList, // 路由白名单
asyncRoutes, // 异步路由
basicRoutes, // 基础路由
getAuthList, // 获取用户权限列表
checkSSOLogin, // 检查oa登录状态
storageType: requestSetting.storageType,// 本地数据存储类型
TOKEN_KEY: requestSetting.tokenKey, // token 存储 key 值
SSO_TOKEN_KEYS: ['SIAMTGT', 'SIAMJWT'], //单点登录相关 token
}
await initPermission(app, options, (params: any) => {
console.log('权限初始化完成===', params)
getCallback(params)
})
}
async function getCallback(params: any) {
if (!params) return null
console.log("🚀 ~permission getCallback:", params)
}
该方法提供插件相关初始化函数。
引入插件插件:
在 src/plugins/init.ts
中动态引入插件:
// 使用路由权限控制
export const initXWPermission = async (app: App) => {
return await import("@/plugins/xw-permission/index").then(async (XWUI: any) => {
XWUI.setupXWPermission(app);
return XWUI
});
}
在 src/main.ts
文件中添加插件初始化方法:
// 使用 路由权限控制
if (VITE_USE_XW_UI_PERMISSION === 'true') {
await initXWPermission(app);
}
使用插件:
在 src/.env
文件中,添加配置 VITE_USE_XW_UI_PERMISSION=true
:
# 是否使用路由权限控制
VITE_USE_XW_UI_PERMISSION=true
如果你想关闭该插件使用,使 VITE_USE_XW_UI_PERMISSION=false
即可。
XElementPlus 组件
XElementPlus 组件总览 | XW-UI 是基于 Element-Plus 二次封装,使用 JSON Scheme 配置化生成式组件库。
Element-Plus 组件
UnoCSS 插件
Micro-App 插件
micro-app 插件是对 MicroApp 的二次封装。由于 micro-app 在微前端应用中,对作为基座的应用和作为子应用的配置和方法不同。该模板为了提供统一的 micro-app 配置,需要对 micro-app 二次封装,以屏蔽在基座应用和子应用时的配置和方法差异。模板提供统一的参数配置,将模板配置为 微前端的基座应用 或 子应用。
国际化
国际化插件是对 vue-i18n 进行了二次封装。修改了 i18n 获取翻译语言和翻译预定义的模式。
默认情况下 vue-i18n 需要创建语言类型和翻译对象,在获取翻译时,通过默认方法传入翻译对象的属性路径获取翻译。
如下示例:
// 定义翻译对象
const i18n = createI18n({
locale: 'ja',
fallbackLocale: 'en',
messages: {
en: {
message: {
hello: 'hello world'
}
},
ja: {
message: {
hello: 'こんにちは、世界'
}
}
}
})
// 获取翻译内容
<p>{{ $t('message.hello', ['hello']) }}</p>
以上模式存在以下问题:
- 当新增一种语言时,需要将预定义所有翻译内容,否则通过
$t
方法查找时会查找失败,导致翻译报错。 - 当新增一个词汇时,需要立即定义翻译内容,否则通过
$t
方法查找时会查找失败,导致翻译报错。
当一个项目随着时间迭代,翻译内容不断增加时,以上问题将突出明显,并大大降低开发效率(每增加一个词汇必须添加对应翻译,将导致开发人员将大量时间花费在词语翻译中)
为了减少开发过程中将大量时间花费在翻译中的问题,我们需要解决以上问题,可以考虑以下方案:
- 翻译文本定义使用对象的属性路径查找方式(不使用
$t('message.login')
)查找方式,而是直接使用翻译文本作为参数。 - 通过预定义默认语言,直接传入
$t
方法为翻译前的文本(而不是翻译文本对象的属性路径),当在翻译文本中查找失败时,回退使用传入的参数。
根据以上问题,需要对 Vue-i18n 进行二次封装。
封装的 i18n 功能:
- 翻译
$t
方法默认传入翻译文本;如翻译词汇登录
,则传入$t('登录')
- 翻译文本定义使用翻译词汇作为 Key。
- 当翻译文本查找失败,默认回退使用传入参数作为翻译。
主题色
mock 联调
vite 插件
vite 插件默认位于 build/vite/plugin
目录下,通过统一的目录管理所有 Vite 插件。
插件分为默认添加的插件和自定义使用的插件,通过 在 src/.env
下配置相关变量可以动态使用 Vite 插件。
插件包括:
插件名称 | 作用 | 使用 |
---|---|---|
vite-imagetools | 对图片做二次处理: 1.提高页面加载速度和性能; 2.适应不同的屏幕大小和设备; 3.防止图片失真或拉伸; 4.调整图像质量; 5.增加图片美观度; | VITE_USE_IMAGETOOLS |
页面
该模板包含基础页面:
- 登录
- 菜单和内容容器
- 404
登录
登录相关功能:
- 登录
- 单点登录
- token 校验
- token 刷新
菜单和容器
菜单与权限相关联,通过权限配置 map 动态生成菜单栏;
菜单栏分为左侧菜单栏和顶部菜单栏,通过配置菜单栏可以显示三种类型
- 仅左侧菜单栏
- 仅顶部菜单栏
- 顶部菜单和左侧菜单
- 当同同时存在顶部菜单栏和左侧菜单时,将顶部菜单设置为一级菜单,左侧菜单设置为二级菜单
登录校验
HTTP 请求使用 XHTTP | XW-UI 插件,该插件是对 Axios 进行二次封装,加入了以下功能:
功能 | 介绍 |
---|---|
消息提示 | 成功/失败/错误等消息和模态弹窗提示 在浏览器端使用消息和模态弹窗提示 在node 端使用 console 打印 |
忽略重复请求 | |
超时重试 | |
加入请求时间戳 | 请求时间戳可以用于调试接口 |
上传文件默认采用 formdata 格式 | 上传文件默认配置: header:{Content-type: FORM_DATA = 'multipart/form-data;charset=UTF-8',} |
固定状态码提示和处理 | |
ajax 错误日志收集钩子 | addAjaxErrorInfo |
全局请求 token | |
token 校验 | |
token 刷新 | 传入 token 过期时间和时间间隔,在每次请求之前自动判断当前时间与 token 过期时间差小于指定刷新间隔时间,如果是,自动请求刷新 Token 接口刷新 Token,并将后续请求缓存;待刷新 Token 接口返回后,使用新的 token 请求缓存的请求。 |
错误清除 token | |
错误退出登录 | |
格式化返回格式 | 格式化返回格式 |
提供请求处理钩子 | |
请求之前处理配置: beforeRequestHook | |
请求成功处理: transformRequestData | |
请求失败处理: requestCatch | |
请求之前的拦截器: requestInterceptors | |
请求之后的拦截器: responseInterceptors | |
请求之前的拦截器错误处理: requestInterceptorsCatch | |
请求之后的拦截器错误处理: responseInterceptorsCatch |
权限和路由
权限模块使用 permission 插件 | XW-UI ,该插件提供以下功能:
功能 | 介绍 |
---|---|
菜单路由权限控制 | 通过接口返回权限路由名称,控制当前登录用户的路由权限 |
按钮级别权限控制 | 通过接口返回按钮权限列表名称,控制当前登录用户的按钮权限 |
单点登录 | 使用当前插件的系统和其他系统相互登录 |
路由划分为异步路由和常规路由:
- 异步路由:动态加载的路由,根据后端返回权限动态加载;
- 常规路由:项目加载时直接加载的路由,不根据权限动态加载,与权限无关,所有用户可见;如:根页面、登录页面、404页面。
- 路由白名单:不需要权限的路由。
- 一般情况将所有常规路由都会添加到路由白名单中,如果你想将异步路由添加到白名单,直接在
src/router/basicRoutes/whiteList.ts
中添加路由名称
- 一般情况将所有常规路由都会添加到路由白名单中,如果你想将异步路由添加到白名单,直接在
添加异步路由:
异步路由位于 src/router/asyncRoutes/module
中,在该文件夹中,以模块名称作为文件夹名称新增文件夹。
添加常规路由:
常规路由位于 src/router/basicRoutes/basicRoutes.ts
中,在该文件中直接添加路由。
样式隔离方案
应用框架使用 样式前缀方式进行样式隔离。
定义样式前缀
样式前缀定义在 src/styles/variables.module
文件中:
// 命名空间
@adminNamespace: mainapp;
// el命名空间
@elNamespace: el;
// 导出变量
:export {
namespace: @adminNamespace;
elNamespace: @elNamespace;
}
@adminNamespace
为应用样式前缀定义。
使用样式前缀
使用样式前缀需要修改两个地方:
- html 标签类名中 使用样式前缀
- css 类名 中使用样式前缀
- html 标签类名中使用样式前缀:
在你的组件/页面中,引入 useDesign
hooks:
import { useDesign } from "@/hooks/web/useDesign";
const { getPrefixCls } = useDesign();
const prefixCls = getPrefixCls("你的组件名称");
在 html 标签中定义类名:
<div :class="[`${prefixCls}-container`]"></div>
- css 类名中使用样式前缀
<style scoped lang="less">
@prefix-cls: ~"@{adminNamespace}-你的组件名称";
.@{prefix-cls}-container {
width: 100%;
height: 100%;
}
</style>
最终形成效果如下: