☰
MeterSphere 前端工程架构与实践指南:Vue3 + Vite + Pinia 的代码组织、提交规范与构建优化
2026/10/2 23:23:22 网站建设 项目流程
  • 质量保障
  • 接口测试
  • 测试
  • 后端
  • 前端
  • AI 应用
  • DevOps

【免费下载链接】metersphere

MeterSphere 是新一代的开源持续测试工具,内置 AI 助手,让软件测试工作更简单、更高效,不再成为持续交付的瓶颈。

项目地址:https://gitcode.com/gh_mirrors/me/metersphere
点击查看免费下载

MeterSphere 是新一代开源持续测试平台,其 frontend 目录承载了整套基于 Vue 3 + Vite + TypeScript 的单页应用。本文以 frontend/README.md 为骨架,结合仓库中真实的配置、源码与构建脚本,系统讲解 MeterSphere 前端的 Git 提交规范、目录架构、Pinia 状态管理、Axios 网络层封装、指令集、hooks、国际化、环境变量、TailwindCSS、TS 配置以及 Vite 构建优化与本地生产调试,帮助开发者在接手或改造该项目时快速对齐约定、少踩坑。

一、Git 提交规范:commitlint 与 Conventional Commits

MeterSphere 前端在 commitlint.config.cjs 中直接继承@commitlint/config-conventional,并在 package.json 的prepare脚本中通过 husky 安装钩子来强制校验 commit 消息,配合lint-staged(package.json)实现提交前自动执行 prettier、eslint、stylelint。

1.1 提交消息格式

仓库约定的标准格式如下:

<Type>[optional Scope]: <Description> [optional body] [optional footer]
  • Type(提交类型):表示提交的目的或影响范围,取值范围包括:
    • feat:新功能(A new feature)
    • fix:修复 bug(A bug fix)
    • docs:文档变更(Documentation changes)
    • style:代码样式调整(Code style changes)
    • refactor:代码重构(Code refactoring)
    • test:测试相关的变更(Test-related changes)
    • chore:构建过程或工具变动(Build process or tooling changes)
    • perf:性能优化(Performance optimization)
    • ci:CI/CD 相关的变动(Changes to the CI/CD configuration or scripts)
    • revert:撤销之前的提交(Revert a previous commit)
  • Scope(作用域):本次提交影响的部分代码或模块,可根据项目需要选择性添加,例如feat(user)表示用户模块的新功能。
  • Description(描述):简明扼要地描述本次提交内容。
  • body(正文):可选的详细描述,可以包含更多信息和上下文。
  • footer(脚注):可选的脚注,通常用于引用相关的问题编号或关闭问题,例如Closes #123。

1.2 示例提交消息

feat(user): add login functionality - Add login form and authentication logic - Implement user authentication API endpoints Closes #123

该示例中,提交类型为feat(新功能),作用域为user,描述了添加登录功能的内容;正文部分提供了更详细的说明,并引用了问题编号。

二、目录架构总览

frontend目录从入口到业务模块做了清晰的职责划分,整体结构如下(frontend/README.md 原文结构,与实际仓库保持一致):

├── babel.config.js // babel配置,支持JSX ├── commitlint.config.js // commitlint配置,校验commit信息 ├── components.d.ts // 组件注册TS声明 ├── config // 项目构建配置 │ ├── plugin // 构建插件 │ ├── utils // 构建工具方法 │ ├── vite.config.base.ts // vite基础配置 │ ├── vite.config.dev.ts // vite开发环境配置 │ └── vite.config.prod.ts // vite 生产配置 ├── index.html // 单页面html模板 ├── src │ ├── App.vue // 应用入口vue │ ├── api // 项目请求api封装 │ │ ├── http // axios封装 │ │ ├── modules // 各业务模块的请求方法 │ │ ├── requrls // 按业务模块划分的接口地址 │ ├── assets // 全局静态资源 │ │ ├── font │ │ ├── icon-font │ │ ├── images │ │ ├── style │ │ ├── svg │ ├── components // 组件 │ ├── config // 全局配置,常量类、JSON │ ├── directive // 自定义指令集 │ ├── enums // 全局枚举定义 │ ├── hooks // 全局hooks集 │ ├── layout // 应用布局组件 │ ├── locale // 国际化配置 │ ├── main.ts // 项目主入口 │ ├── models // 全局数据模型定义 │ ├── router // 路由 │ ├── store // pinia状态库 │ ├── types // 全局TS声明 │ ├── utils // 公共工具方法 │ └── views // 页面模块 │ ├── modules // 页面模块 │ └── base // 公共页面,403、404等 ├── env.d.ts // 环境信息TS类型声明 └── .env.development // 开发环境变量声明 └── .env.production // 生产环境变量声明 └── .eslintrc.js // eslint配置 └── .prettierrc.js // prettier配置 └── tsconfig.json // 全局TS配置

需要说明的是:README 中的src/views下标注为modules与base的划分,在实际仓库中体现为按业务模块命名的目录(如api-test、bug-management、case-management、setting、test-plan、workbench等),以及存放公共页面的base目录(404、403、500、not-found等),从源码结构看二者是同一套约定:按功能模块划分页面,公共页面单独收敛。

构建配置实际位于frontend/config下,由 vite.config.base.ts、vite.config.dev.ts、vite.config.prod.ts 三份文件分别管理基础、开发、生产三段配置,并支持通过--mode参数切换自定义环境。

三、状态管理模块设计(Pinia)

MeterSphere 前端采用Pinia作为 Vue3 状态管理方案,其 API 风格与 Redux 类状态管理库类似,通过模块化方式注册模块store,每个 store 包含数据仓库state、数据包装过滤getter、同步/异步操作action。与 Vuex 相比,Pinia 提供了 Composition API、完整支持 TS 以及可扩展的 Plugin 功能。

整体模块划分为业务模块modules/*、注册入口index以及插件plugins/*。在 store/index.ts 中声明注册 pinia 并引入自定义插件:

import { createPinia } from 'pinia'; import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'; import { debouncePlugin } from './plugins'; import useXXStore from './modules/xx'; const pinia = createPinia(); // 插件会在实例创建后应用,因此插件不能在pinia实例创建前使用 pinia.use(debouncePlugin).use(piniaPluginPersistedstate); export { useXXStore }; // 导出模块store export default pinia;

然后在项目入口main.ts中使用:

import { createApp } from 'vue'; import store from '@/store'; import app from '@/App.vue'; createApp(app).use(store).mount('#app');

3.1 plugins:基于 lodash 的防抖插件

在 store/plugins.ts 内编写 pinia 插件,并在注册时引入使用。由于 Pinia 的 TS 类型声明中每个 store 只有三个原生属性state、getters、actions,使用额外属性时需先声明:

import { debounce } from 'lodash-es'; import type { PiniaPluginContext } from 'pinia'; // 首先得声明插件使用到的额外属性 declare module 'pinia' { export interface DefineStoreOptionsBase<S, Store> { debounce?: Partial<Record<keyof StoreActions<Store>, number>>; // 防抖配置 } } // 基于lodash的防抖函数封装,读取store中debounce配置的属性,针对已配置的属性更改操作进行防抖处理 export const debouncePlugin = ({ options, store }: PiniaPluginContext): void | Record<string, any> => { if (options.debounce) { return Object.keys(options.debounce).reduce((debounceActions: Record<string, any>, action) => { debounceActions[action] = debounce(store[action], options.debounce![action]); return debounceActions; }, {}); } };

在 store 中按如下方式配置防抖:

export const useStore = defineStore('demo', { state: () => ({ testDebounce: '', }), getters: {}, actions: {}, debounce: { testDebounce: 500, // 值为防抖缓冲时间(毫秒) }, });

除了自研的debouncePlugin,store/index.ts 还通过pinia-plugin-persistedstate为状态提供持久化能力。实际仓库中导出的模块 store 包括useAppStore、useGlobalStore、useMinderStore、useUserStore、useVisitStore等(见 store/modules 目录)。

四、网络模块设计(Axios 三层封装)

网络模块包含三层:请求 url 封装api/requrls/*、请求方法封装api/modules/*、请求工具封装api/http/*。

4.1 requrls:接口地址收敛管理

将项目接口地址收敛至此文件夹下管理,避免出现多个重复接口 url、方便接口地址复用且方便统一处理:

export const LoginUrl = '/api/user/login'; export const LogoutUrl = '/api/user/logout'; export const GetUserInfoUrl = '/api/user/info'; export const GetMenuListUrl = '/api/user/menu';

实际仓库中api/requrls下按api-test、bug-management、case-management、project-management、setting、test-plan、user等业务模块拆分,例如 requrls/user.ts。

4.2 modules:请求方法与接口地址解耦

将实际请求方法按业务模块划分,统一管理,与接口地址解耦:

import axios from 'axios'; import { LoginUrl, LogoutUrl, GetUserInfoUrl, GetMenuListUrl } from '@/api/requrls/user'; import type { RouteRecordNormalized } from 'vue-router'; import type { UserState } from '@/store/modules/user/types'; import type { LoginData, LoginRes } from '@/models/user'; export function login(data: LoginData) { return axios.post<LoginRes>(LoginUrl, data); } export function logout() { return axios.post<LoginRes>(LogoutUrl); } export function getUserInfo() { return axios.post<UserState>(GetUserInfoUrl); } export function getMenuList() { return axios.post<RouteRecordNormalized[]>(GetMenuListUrl); }

最终通过index.ts将请求方法暴露出去:

export * from './modules/user'; export * from './modules/dashboard'; export * from './modules/message';

4.3 请求工具封装(axios 实例与拦截器)

在 api/http/index.ts 中,基于axios封装了统一的MSAxios实例,提供form-data/json/urlencoded格式的数据处理、自定义头部处理、响应拦截错误处理、分级提示(modal、message、none)以及 GET 请求防缓存。

// 分级错误信息提示,none为静默模式即不提示、modal为对话框提示、message为tips消息提示 // ErrorMessageMode默认为message模式,通过传入请求方法的options.errorMessageMode入参判断 export type ErrorMessageMode = 'none' | 'modal' | 'message' | undefined; // 三种入参数据格式 export enum ContentTypeEnum { // json JSON = 'application/json;charset=UTF-8', // form-data qs序列化处理 FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8', // form-data upload文件流处理 FORM_DATA = 'multipart/form-data;charset=UTF-8', } // 对上传文件请求入参格式额外定义 export interface UploadFileParams { data?: Recordable; // 除文件流外的参数 name?: string; // 文件流字段名 file: File | Blob; // 文件内容 filename?: string; // 文件名 [key: string]: any; // 其他params } // 基准返回数据格式 export interface Result<T = any> { code: number; type: 'success' | 'error' | 'warning'; message: string; result: T; }

实例默认配置了baseURL(${window.location.origin}/${import.meta.env.VITE_API_BASE_URL})、超时时间300 * 1000(毫秒)与Content-Type,并通过requestOptions提供细粒度开关(见 api/http/index.ts),例如:

  • isReturnNativeResponse:是否返回原生响应头;
  • isTransformResponse:是否对返回数据进行处理;
  • joinParamsToUrl:post 请求时是否添加参数到 url;
  • errorMessageMode:错误提示模式(none/modal/message);
  • joinTime:是否加入时间戳防缓存;
  • withToken:是否携带 token。

beforeRequestHook会对 GET 请求自动追加时间戳参数(_t),该逻辑位于 helper.ts:非 restful 时返回{ _t: now },restful 风格时返回?_t=${now}。请求拦截器(requestInterceptors)会从本地 token 中取出sessionId与csrfToken,注入X-AUTH-TOKEN、CSRF-TOKEN、Accept-Language、ORGANIZATION、PROJECT等请求头,实现登录态、国际化与组织/项目上下文的下发。

五、directive 指令集

指令入口index.ts导入并注册定义的全部指令(见 directive/index.ts):

import { App } from 'vue'; import permission from './permission'; export default { install(Vue: App) { Vue.directive('permission', permission); }, };

权限指令(或其他自定义指令)在同级目录下创建,以指令名称命名文件夹,例如 directive/permission/index.ts:

import { DirectiveBinding } from 'vue'; import { useUserStore } from '@/store'; function checkPermission(el: HTMLElement, binding: DirectiveBinding) { const { value } = binding; const userStore = useUserStore(); const { role } = userStore; if (Array.isArray(value)) { if (value.length > 0) { const permissionValues = value; const hasPermission = permissionValues.includes(role); if (!hasPermission && el.parentNode) { el.parentNode.removeChild(el); } } } else { throw new Error(`need roles! Like v-permission="['admin','user']"`); } } export default { mounted(el: HTMLElement, binding: DirectiveBinding) { checkPermission(el, binding); }, updated(el: HTMLElement, binding: DirectiveBinding) { checkPermission(el, binding); }, };

该指令通过mounted与updated两个生命周期钩子对元素进行权限校验,不满足权限时直接从 DOM 中移除元素。实际仓库中还包含 outerClick、validateExpiration、validateLicense 等指令,分别处理点击外部关闭、有效期校验与 License 校验场景。

六、hooks:业务逻辑抽象

全局抽象钩子集(与 Vue2 的 mixins 类似),只写业务逻辑的钩子。项目大量复用@vueuse/core提供的钩子函数,避免重复造轮子;在编写钩子功能前,建议先到 vueuse 的 Function List 确认是否已有相同功能。导出钩子采用export default function useXxx命名规范,以权限钩子为例(hooks/usePermission.ts):

import { RouteLocationNormalized, RouteRecordRaw } from 'vue-router'; import { useUserStore } from '@/store'; export default function usePermission() { const userStore = useUserStore(); return { accessRouter(route: RouteLocationNormalized | RouteRecordRaw) { // do something }, findFirstPermissionRoute(_routers: any, role = 'admin') { // do something }, // You can add any rules you want }; }

实际仓库的 hooks 目录中已沉淀了大量可复用钩子,如useTableStore、useLocalForage、useModal、useI18n、usePermission、useResponsive、useWebsocket等,页面开发时可直接引用。

七、layout:布局组件

项目布局设计模块,将最上层布局组件放置此文件夹内,统一管理项目各类上层布局(例如左侧菜单-右侧内容布局、顶层菜单-中间内容-底部页脚布局等)。典型的路由页面容器示例:

<template> <!-- router-view内为实际路由切换变化的内容,视为路由页面容器 --> <router-view v-slot="{ Component, route }"> <!-- transition为页面切换时提供进出动画,平滑过渡页面渲染 --> <transition name="fade" mode="out-in" appear> <component :is="Component" v-if="!route.meta.isCache" :key="route.fullPath" /> <!-- keep-alive提供组件状态缓存,以便快速渲染组件内容 --> <keep-alive v-else> <component :is="Component" :key="route.fullPath" /> </keep-alive> </transition> </router-view> </template>

该模板通过route.meta.isCache决定是否启用keep-alive缓存,配合transition提供平滑的页面切换动画。实际仓库中的 layout 目录包含default-layout.vue、full-page-layout.vue、no-permission-layout.vue、page-layout.vue、share-layout.vue、single-logo-layout.vue等布局,分别对应登录、工作台、分享页、无权限页等不同场景。

八、models:全局数据模型

全局数据模型将涉及请求、组件 props、状态库的公共属性抽象为数据模型,在此文件夹内声明,保证全局数据的类型统一、方便维护。请求模型示例:

export interface HttpResponse<T = unknown> { status: number; msg: string; code: number; data: T; }

也可以是业务相关模型:

export interface LoginData { username: string; password: string; } export interface LoginRes { token: string; }

实际仓库中 models 按业务模块组织,例如 models/user.ts 定义用户登录相关模型,models/apiTest、models/setting 等分别承载对应业务的数据结构。

九、router:路由管理

项目路由管理模块:

  • 入口文件index.ts注册并暴露全部路由;
  • 以模块命名文件夹划分模块路由,放置在routes/*下(见 router/routes/modules);
  • guard/*下放置路由导航控制,包含权限、登录重定向等(见 router/guard);
  • app-menus/index.ts为菜单相关的路由信息;
  • constants.ts定义路由常量,包括路由白名单、重定向路由名、默认主页路由信息等。

十、enums:全局枚举

将可枚举的类型统一管理,以类型+Enum命名,方便维护。示例如下(对应 enums/httpEnum.ts 等文件):

/** * @description: Request result set */ export enum ResultEnum { SUCCESS = 0, ERROR = 1, TIMEOUT = 401, TYPE = 'success', } /** * @description: request method */ export enum RequestEnum { GET = 'GET', POST = 'POST', PUT = 'PUT', DELETE = 'DELETE', } /** * @description: contentTyp */ export enum ContentTypeEnum { JSON = 'application/json;charset=UTF-8', FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8', FORM_DATA = 'multipart/form-data;charset=UTF-8', }

实际仓库的 enums 目录包含httpEnum.ts、apiEnum.ts、caseEnum.ts、routeEnum.ts、testPlanEnum.ts、workbenchEnum.ts等按业务域划分的枚举文件。

十一、locale:国际化

国际化模块存放项目声明的国际化配置,按语种划分模块。模块入口文件为index.ts,负责定义菜单、导航栏等公共的国际化配置,其他按系统功能声明并导入index.ts;页面组件的国际化配置在页面的目录下声明,如views/dashboard/workbench/locale。入口实现(对应 locale/index.ts):

import { createI18n } from 'vue-i18n'; import en from './en-US'; import cn from './zh-CN'; export const LOCALE_OPTIONS = [ { label: '中文', value: 'zh-CN' }, { label: 'English', value: 'en-US' }, ]; const defaultLocale = localStorage.getItem('MS-locale') || 'zh-CN'; const i18n = createI18n({ locale: defaultLocale, fallbackLocale: 'en-US', legacy: false, allowComposition: true, messages: { 'en-US': en, 'zh-CN': cn, }, }); export default i18n;

该实现支持中英文双语,默认语言从localStorage的MS-locale读取(默认zh-CN),并通过fallbackLocale: 'en-US'兜底。实际仓库中 locale/zh-CN 与 locale/en-US 下按common.ts、sys.ts、settings.ts、index.ts组织文案。

十二、types 与 utils

  • types:项目级别的类型声明,与业务无关,与models、enums不同,这里声明的是项目模块级别的类型或工具模块的类型声明,例如axios的配置声明、第三方插件不提供 TS 支持但需要我们自定义的声明等。实际仓库中 types 目录包含axios.d.ts、global.d.ts、table.d.ts、window.d.ts等。
  • utils:公共方法、工具,按功能类型命名文件,单个文件内工具方法的功能要对应命名。实际仓库的 utils 目录包含auth.ts(token 存取)、is.ts(类型判断)、tree.ts、xpath.ts、serializeMap.ts等。

十三、views:页面模块

页面模块按功能模块划分,公共模块有login、base,其中base模块内包含 404、403 页面等。实际仓库中 views 下的业务模块包括api-test、bug-management、case-management、project-management、setting、taskCenter、test-plan、workbench等,公共页面集中在 views/base(no-project、no-resource、not-found、redirect等)与 views/exception(403、404、500)。

十四、主题配置

主题配置流程如下:

  1. 去 Design Lab 创建主题(https://arco.design/themes/home);
  2. 主题以ms-theme-命名开头;
  3. 点击页面的配置主题,采用"CSS 变量" + "Tailwind 配置变量" + "基于 css 变量自行计算混合色覆盖 arco-theme 变量"的组合方案。

实际仓库中,theme/default.less 与 theme/green.less 提供了默认与绿色两套主题变量,assets/style/var.less 定义了全局 less 变量,并通过 vite.config.base.ts 的css.preprocessorOptions.less.modifyVars注入到所有 less 样式中。

十五、.env.* 环境变量配置

Vite 内置了环境变量配置功能,只需在项目根目录下创建以.env.*开头的文件即可。默认.env为生产环境、.env.development为开发环境、.env.XXX为自定义XXX环境(自定义环境需要在package.json的项目运行命令后加入--mode XXX)。各类环境变量配置示例如下(注意:环境变量文件内使用注释要用#,不能使用//):

# .env.production 为生产环境配置 NODE_ENV=production # 代码中通过import.meta.env.NODE_ENV访问 VITE_STG=1 # 自定义变量必须用VITE_开头,代码中通过import.meta.env.VITE_XXX访问 # .env.development 为开发环境配置 NODE_ENV=development VITE_APP_ENV = dev VITE_APP_TITLE = 我是标题 # .env.XXX 为自定义环境配置 VITE_MYENV = 1 # 代码中通过import.meta.env.VITE_MYENV访问

上述环境变量在代码中都可以用import.meta.env.VITE_XXX访问,但在vite.config.ts配置文件中无法使用此方法访问——原因是该访问链是 vite 在初始化后通过读取本地.env.XXX文件并注入到import.meta中的,而在vite.config.ts中 vite 此时还未初始化。应通过下面的方法访问:

import { defineConfig, loadEnv } from 'vite'; // 导入loadEnv方法 loadEnv(mode, process.cwd()).VITE_XXX; // 在需要访问env里变量的地方使用此方法访问即可

在实际仓库中,vite.config.dev.ts 通过dotenv.config({ path: ['.env.development.local', '.env.development'] })注入本地/开发配置环境变量(先导入的配置优先级高),并据此读取VITE_DEV_DOMAIN配置开发代理;package.json 中的脚本分别以--config指定配置文件、以--mode指定环境模式:dev、build:local(--mode development)、build:localProd(--mode prod)、report(通过cross-env REPORT=true开启分析模式)。

十六、TailwindCSS 配置

module.exports = { content: ['./index.html', './src/**/*.{html,js,vue}', './src/*.{html,js,vue}'], // 需要解析的文件路径 theme: { // 自定义主题配置 backgroundColor: { // 自定义背景色 menuHover: '#272D39', headerBg: '#191E29', }, textColor: (theme) => ({ // 自定义字体颜色 ...theme('colors'), // 这里必须解构原本有的颜色,不然会导致在页面style中使用@apply应用内部字体颜色类的时候报错找不到内部字体颜色类 '40Gray': 'rgba(255,255,255,0.40)', '65Gray': 'rgba(255,255,255,0.65)', }), extend: {}, }, plugins: [], };

实际仓库的 tailwind.config.mjs 中,content同样覆盖了index.html与src下的 vue/js 文件,且通过@apply使用内部字体颜色类时必须先解构theme('colors'),否则会报"找不到内部字体颜色类"的错误。

十七、TS 配置:tsconfig.json

tsconfig.json 实际配置与 README 基本一致,并对 include 做了扩展:

{ "include": [ "src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", "src/components.d.ts", "src/auto-imports.d.ts", "types/**/*.d.ts", "types/**/*.ts", "build/**/*.ts", "mock/**/*.ts", "__test__/**/*.ts", "node_modules/monaco-editor/monaco.d.ts", "src/views/test-plan/report/detail/alibabapuhuiti.js" ], "exclude": ["node_modules"], "compilerOptions": { "allowJs": true, "noEmit": true, "target": "esnext", "useDefineForClassFields": true, "allowSyntheticDefaultImports": true, "module": "esnext", "moduleResolution": "node", "strict": true, "jsx": "preserve", "sourceMap": true, "resolveJsonModule": true, "isolatedModules": true, "esModuleInterop": true, "lib": ["esnext", "dom"], "skipLibCheck": true, "types": ["node"], "baseUrl": ".", "paths": { "@/*": ["./src/*"], "#/*": ["types/*"] } } }

关键配置说明:

  • allowJs: true:允许编译器编译 JS、JSX 文件;
  • noEmit: true:仅做类型检查,不输出编译产物(配合vue-tsc --noEmit的构建前类型检查);
  • target/module: esnext:使用 ES 最新语法与 ES 模块语法;
  • strict: true:开启严格模式;
  • paths路径映射:@/*指向./src/*,#/*指向types/*,与代码中import ... from '@/...'、from '#/...'的写法对应。

十八、Vite 配置:插件详解

Vite 构建生产资源使用 rollup 打包,因此可以在 Vite 中使用 rollup 支持的所有插件。

18.1 组件自动导入与按需加载

使用unplugin-auto-import/vite插件实现自动导入、unplugin-vue-components/vite插件按需导入自定义组件,具体用法如下:

import AutoImport from 'unplugin-auto-import/vite' import Components from 'unplugin-vue-components/vite' import { ArcoResolver } from 'unplugin-vue-components/resolvers' import { vitePluginForArco } from '@arco-plugins/vite-vue' export default () => defineConfig({ plugins: [ // 自定义组件自动引入 AutoImport({ dts: 'src/auto-import.d.ts', // 输出声明文件地址(使用typescript时必须配置,不然会导致页面使用未导入的组件时报错) resolvers: [ElementPlusResolver()], // ElementPlus自动导入 }), // 自定义组件按需引入 Components({ dts: 'src/components.d.ts', // 输出声明文件地址 dirs: ['src/components'], // 按需加载的文件夹 resolvers: [ ArcoResolver(), // ArcoDesignVue按需加载 ], }), // 样式自动导入 vitePluginForArco({}) ] })

在实际仓库中,基础配置 vite.config.base.ts 的AutoImport配置了imports: ['vue'],将 Vue 的常用 API 自动注入到.ts/.tsx/.vue/.md文件并生成src/auto-import.d.ts;Arco 组件按需加载由 plugin/arcoResolver.ts 封装,其中dirs: []特意避免解析src/components下的业务组件。

18.2 SVG 图标自动加载

使用vite-plugin-svg-icons插件实现自动加载 svg 图片,并通过封装 svg 组件的方式一行代码使用:

import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'; export default () => defineConfig({ plugins: [ createSvgIconsPlugin({ // 指定svg读取的文件夹 iconDirs: [resolve(process.cwd(), 'src/assets/icons/svg')], // 指定icon的读取名字,使用svg文件名为icon名字 symbolId: 'icon-[dir]-[name]', }), ], });

实际仓库中 vite.config.base.ts 的createSvgIconsPlugin读取src/assets/svg与public/images两个目录,symbolId使用icon-[name]格式;此外还通过svgLoader(vite-svg-loader)支持直接在组件中以组件方式引用 svg 文件。

18.3 环境读取与体积分析

使用 Vite 自带插件loadEnv读取环境信息做环境判断,使用rollup-plugin-visualizer插件分析打包后的文件体积:

import { defineConfig, loadEnv } from 'vite'; import { visualizer } from 'rollup-plugin-visualizer'; // 这里的mode参数为package.json文件配置的环境参数,使用`--mode XXX`,如"report: rimraf dist && vite build --mode analyze" export default ({ mode }) => defineConfig({ plugins: [ // 这里通过--mode analyze配置为分析模式,使用visualizer插件分析方法,输出report.html分析报告 loadEnv(mode, process.cwd()).VITE_ANALYZE === 'Y' ? visualizer({ open: true, brotliSize: true, filename: 'report.html' }) : null, ], });

实际仓库中 plugin/visualizer.ts 通过 config/utils/index.ts 的isReportMode()判断是否开启分析(对应npm run report脚本),分析报告输出到node_modules/.cache/visualizer/stats.html,同时统计gzipSize与brotliSize。

18.4 其他生产构建插件

plugin/compress.ts 使用vite-plugin-compression输出 gzip(.gz)或 brotli(.br)压缩产物;plugin/imagemin.ts 使用vite-plugin-imagemin对 gif/png/jpeg/svg 进行压缩(如 mozjpegquality: 20、pngquantquality: [0.8, 0.9]);vite.config.prod.ts 还通过@vitejs/plugin-legacy依据browserslist(> 0.5%、last 2 versions、not IE 11)做旧浏览器兼容。

十九、Vite 配置:build 分包详解

在 Vite 构建生产资源时,通过rollupOptions.output.manualChunks配置分包策略(与 webpack 分包机制类似):

import { mergeConfig } from 'vite'; import baseConfig from './vite.config.base'; import configCompressPlugin from './plugin/compress'; import configVisualizerPlugin from './plugin/visualizer'; import configArcoResolverPlugin from './plugin/arcoResolver'; import configImageminPlugin from './plugin/imagemin'; export default mergeConfig( { mode: 'production', plugins: [ configCompressPlugin('gzip'), configVisualizerPlugin(), configArcoResolverPlugin(), configImageminPlugin(), ], build: { rollupOptions: { output: { manualChunks: { arco: ['@arco-design/web-vue'], chart: ['echarts', 'vue-echarts'], vue: ['vue', 'vue-router', 'pinia', '@vueuse/core', 'vue-i18n'], }, }, }, chunkSizeWarningLimit: 2000, }, }, baseConfig );

实际仓库的 vite.config.prod.ts 将分包扩展为四组:vue(vue、vue-router、pinia、@vueuse/core、vue-i18n)、arco(@arco-design/web-vue)、chart(echarts、vue-echarts)、codeEditor(monaco-editor),并将chunkSizeWarningLimit设为 2000(KB),避免大体积 chunk 的告警干扰。将基础库、UI 组件库、图表库、代码编辑器独立成 chunk,可以充分利用浏览器缓存,提升首屏加载性能。

二十、本地生产环境调试

需先安装 docker(选择对应系统版本安装),然后在frontend目录下执行:

cd frontend/ pnpm run build:local docker build -t metersphere/ms-v3 . docker run -d -p 5100:5100 --name ms-v3 metersphere/ms-v3

其中build:local脚本会先执行vue-tsc --noEmit做类型检查,再以vite build --config ./config/vite.config.prod.ts --mode development构建本地生产包;随后通过 Docker 镜像metersphere/ms-v3启动容器,将宿主机的5100端口映射到容器内5100端口(--name ms-v3为容器命名),即可在浏览器中访问本地生产构建产物进行验证。仓库根目录还提供了 frontend/Dockerfile 与 frontend/nginx.conf,用于容器化部署与 Nginx 托管静态资源。

结语

MeterSphere 前端在工程化上形成了完整的约定体系:Conventional Commits 提交规范约束协作流程,api/requrls + modules + http三层网络封装统一请求行为,Pinia 插件机制(防抖 + 持久化)扩展状态管理能力,Vite 插件矩阵与 manualChunks 分包策略优化构建产物,环境变量、国际化、主题、TailwindCSS 与 TS 配置则保证了多环境、多语言、多主题下的可维护性。理解这套设计,无论是新增业务页面、扩展请求封装,还是定制构建流程,都能在既有框架内快速落地。

  • 质量保障
  • 接口测试
  • 测试
  • 后端
  • 前端
  • AI 应用
  • DevOps

【免费下载链接】metersphere

MeterSphere 是新一代的开源持续测试工具,内置 AI 助手,让软件测试工作更简单、更高效,不再成为持续交付的瓶颈。

项目地址:https://gitcode.com/gh_mirrors/me/metersphere
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询