Files
gym-manage/docs/design/前端工程化建设文档.md
T
2026-03-05 13:48:13 +08:00

929 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 健身房管理系统前端工程化建设文档
> 文档编号: GYM-FE-ENG-001
> 版本: v1.0
> 日期: 2026-03-04
> 作者: 张翔
> 状态: 初稿
---
## 文档修订历史
| 版本 | 日期 | 作者 | 修订内容 |
| ---- | ---------- | ---- | -------- |
| v1.0 | 2026-03-04 | 张翔 | 创建前端工程化建设文档 |
---
## 参考文档
- 《健身房管理系统前端技术架构详细设计》 GYM-FE-ARCH-001
- 《健身房管理系统前端开发规范》 GYM-FE-DEV-001
- Vite 官方文档
- GitHub Actions 文档
---
## 一、工程化概述
### 1.1 工程化目标
- **提高开发效率**:自动化重复性工作,减少手动操作
- **保证代码质量**:通过自动化检查和测试,确保代码质量
- **统一开发规范**:通过工具强制执行代码规范
- **简化部署流程**:自动化构建和部署,减少人为错误
- **提升团队协作**:统一开发环境和工具链
### 1.2 工程化体系
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 前端工程化体系 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 开发工具链 │ │
│ ├─────────────────────────────────────────────────────────────────┤ │
│ │ • Node.js • npm/yarn • Git • VSCode │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 构建工具 │ │
│ ├─────────────────────────────────────────────────────────────────┤ │
│ │ • Vite • TypeScript • ESLint • Prettier │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 代码质量工具 │ │
│ ├─────────────────────────────────────────────────────────────────┤ │
│ │ • Husky • Commitlint • Lint-staged • Stylelint │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 测试工具 │ │
│ ├─────────────────────────────────────────────────────────────────┤ │
│ │ • Vitest • Playwright • Coverage • Testing Library │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ CI/CD工具 │ │
│ ├─────────────────────────────────────────────────────────────────┤ │
│ │ • GitHub Actions • Docker • Nginx • CDN │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## 二、构建工具配置
### 2.1 Vite配置
#### 2.1.1 基础配置
```typescript
// vite.config.ts
import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd())
return {
plugins: [vue()],
resolve: {
alias: {
'@': resolve(__dirname, 'src'),
'@components': resolve(__dirname, 'src/components'),
'@utils': resolve(__dirname, 'src/utils'),
'@api': resolve(__dirname, 'src/api'),
'@stores': resolve(__dirname, 'src/stores')
}
},
server: {
port: 5173,
host: true,
open: true,
proxy: {
'/api': {
target: env.VITE_API_BASE_URL,
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
},
build: {
outDir: 'dist',
assetsDir: 'assets',
sourcemap: mode === 'development',
minify: 'terser',
terserOptions: {
compress: {
drop_console: mode === 'production',
drop_debugger: mode === 'production',
pure_funcs: mode === 'production' ? ['console.log', 'console.info'] : []
}
},
rollupOptions: {
output: {
manualChunks: {
'vue-vendor': ['vue', 'vue-router', 'pinia'],
'element-plus': ['element-plus'],
'utils': ['lodash-es', 'dayjs'],
'crypto': ['crypto-js', 'jsencrypt']
}
}
},
chunkSizeWarningLimit: 1000
},
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/assets/styles/variables.scss";`
}
}
}
}
})
```
#### 2.1.2 插件配置
```typescript
// vite.config.ts
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import Compression from 'vite-plugin-compression'
import { visualizer } from 'rollup-plugin-visualizer'
export default defineConfig({
plugins: [
vue(),
AutoImport({
imports: ['vue', 'vue-router', 'pinia'],
dts: 'src/auto-imports.d.ts',
eslintrc: {
enabled: true
}
}),
Components({
resolvers: [ElementPlusResolver()],
dts: 'src/components.d.ts'
}),
Compression({
verbose: true,
disable: false,
threshold: 10240,
algorithm: 'gzip',
ext: '.gz'
}),
visualizer({
open: false,
gzipSize: true,
brotliSize: true
})
]
})
```
### 2.2 TypeScript配置
```json
// tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"],
"@utils/*": ["src/utils/*"],
"@api/*": ["src/api/*"],
"@stores/*": ["src/stores/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
}
```
### 2.3 环境变量配置
```typescript
// .env.development
VITE_APP_TITLE=()
VITE_API_BASE_URL=http://localhost:8080/api
VITE_UPLOAD_URL=http://localhost:8080/upload
VITE_WS_URL=ws://localhost:8080/ws
VITE_SENTRY_DSN=
VITE_CRYPTO_SECRET_KEY=your-secret-key-here
VITE_RSA_PUBLIC_KEY=your-rsa-public-key-here
// .env.production
VITE_APP_TITLE=
VITE_API_BASE_URL=https://api.example.com/api
VITE_UPLOAD_URL=https://api.example.com/upload
VITE_WS_URL=wss://api.example.com/ws
VITE_SENTRY_DSN=https://xxx@sentry.io/xxx
VITE_CRYPTO_SECRET_KEY=your-production-secret-key-here
VITE_RSA_PUBLIC_KEY=your-production-rsa-public-key-here
// .env.staging
VITE_APP_TITLE=()
VITE_API_BASE_URL=https://staging-api.example.com/api
VITE_UPLOAD_URL=https://staging-api.example.com/upload
VITE_WS_URL=wss://staging-api.example.com/ws
VITE_SENTRY_DSN=https://xxx@sentry.io/xxx
VITE_CRYPTO_SECRET_KEY=your-staging-secret-key-here
VITE_RSA_PUBLIC_KEY=your-staging-rsa-public-key-here
```
---
## 三、代码规范工具
### 3.1 ESLint配置
```json
// .eslintrc.json
{
"extends": [
"plugin:vue/vue3-recommended",
"plugin:@typescript-eslint/recommended",
"prettier",
"plugin:prettier/recommended"
],
"parser": "vue-eslint-parser",
"parserOptions": {
"ecmaVersion": "latest",
"parser": "@typescript-eslint/parser",
"sourceType": "module"
},
"plugins": ["vue", "@typescript-eslint", "prettier"],
"rules": {
"vue/multi-word-component-names": "off",
"vue/no-v-html": "warn",
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-unused-vars": [
"error",
{
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^_"
}
],
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"no-console": [
"warn",
{
"allow": ["warn", "error"]
}
],
"no-debugger": "error",
"prettier/prettier": "error"
}
}
```
### 3.2 Prettier配置
```json
// .prettierrc
{
"semi": false,
"singleQuote": true,
"printWidth": 100,
"trailingComma": "es5",
"arrowParens": "avoid",
"endOfLine": "lf",
"tabWidth": 2,
"useTabs": false,
"bracketSpacing": true,
"jsxSingleQuote": false,
"proseWrap": "preserve"
}
```
### 3.3 Stylelint配置
```json
// .stylelintrc.json
{
"extends": ["stylelint-config-standard", "stylelint-config-prettier"],
"rules": {
"selector-class-pattern": "^[a-z][a-zA-Z0-9-__]*$",
"selector-pseudo-class-no-unknown": [
true,
{
"ignorePseudoClasses": ["deep", "global"]
}
],
"selector-pseudo-element-no-unknown": [
true,
{
"ignorePseudoElements": ["v-deep", "v-global", "v-slotted"]
}
]
}
}
```
---
## 四、自动化工具
### 4.1 Husky配置
```json
// package.json
{
"scripts": {
"prepare": "husky install",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
"format": "prettier --write src/",
"lint:style": "stylelint \"src/**/*.{css,scss,vue}\" --fix"
}
}
```
```bash
# 初始化Husky
npx husky install
# 添加pre-commit钩子
npx husky add .husky/pre-commit "npx lint-staged"
# 添加commit-msg钩子
npx husky add .husky/commit-msg "npx commitlint --edit $1"
```
### 4.2 Lint-staged配置
```json
// .lintstagedrc.json
{
"*.{js,jsx,ts,tsx,vue}": [
"eslint --fix",
"prettier --write"
],
"*.{css,scss,vue}": [
"stylelint --fix"
],
"*.{json,md}": [
"prettier --write"
]
}
```
### 4.3 Commitlint配置
```javascript
// commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'chore', 'ci']
],
'type-case': [2, 'always', 'lower-case'],
'type-empty': [2, 'never'],
'scope-case': [2, 'always', 'lower-case'],
'subject-case': [2, 'never', ['sentence-case', 'start-case', 'pascal-case', 'upper-case']],
'subject-empty': [2, 'never'],
'subject-full-stop': [2, 'never', '.'],
'header-max-length': [2, 'always', 100]
}
}
```
---
## 五、CI/CD流程
### 5.1 GitHub Actions配置
```yaml
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run ESLint
run: npm run lint
- name: Run Prettier check
run: npm run format:check
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run unit tests
run: npm run test:unit
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: ./coverage/coverage-final.json
fail_ci_if_error: true
build:
runs-on: ubuntu-latest
needs: [lint, test]
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
name: dist
path: dist/
```
### 5.2 CD配置
```yaml
# .github/workflows/cd.yml
name: CD
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Deploy to server
uses: easingthemes/ssh-deploy@v3
with:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
REMOTE_USER: ${{ secrets.REMOTE_USER }}
TARGET: /var/www/gym-manage/frontend
SOURCE: dist/
```
---
## 六、项目脚手架
### 6.1 项目初始化
```bash
# 创建新项目
npm create vite@latest gym-manage-frontend -- --template vue-ts
# 进入项目目录
cd gym-manage-frontend
# 安装依赖
npm install
# 安装开发依赖
npm install -D \
@vitejs/plugin-vue \
unplugin-auto-import \
unplugin-vue-components \
sass \
eslint \
@typescript-eslint/parser \
@typescript-eslint/eslint-plugin \
eslint-plugin-vue \
prettier \
eslint-config-prettier \
eslint-plugin-prettier \
husky \
lint-staged \
@commitlint/cli \
@commitlint/config-conventional \
vitest \
@vue/test-utils \
@playwright/test \
rollup-plugin-visualizer \
vite-plugin-compression
# 安装生产依赖
npm install \
vue \
vue-router \
pinia \
axios \
dayjs \
lodash-es \
element-plus \
dompurify \
crypto-js \
jsencrypt \
web-vitals
```
### 6.2 目录结构初始化
```bash
# 创建目录结构
mkdir -p src/{api,assets/{images,icons,styles},components/{base,business,layout},composables,config,directives,hooks,layouts,router,stores,types,utils,views}
mkdir -p src/test/{unit,e2e}
mkdir -p public
# 创建配置文件
touch .env.development .env.production .env.staging
touch .eslintrc.json .prettierrc .stylelintrc.json
touch tsconfig.json tsconfig.node.json
```
### 6.3 基础文件创建
```typescript
// src/main.ts
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
import router from './router'
import './assets/styles/main.scss'
const app = createApp(App)
const pinia = createPinia()
app.use(pinia)
app.use(router)
app.use(ElementPlus)
app.mount('#app')
```
```typescript
// src/App.vue
<template>
<router-view />
</template>
<script setup lang="ts">
</script>
<style>
#app {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
</style>
```
---
## 七、开发工具链
### 7.1 VSCode配置
```json
// .vscode/settings.json
{
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.fixAll.stylelint": true
},
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue"
],
"typescript.tsdk": "node_modules/typescript/lib",
"volar.takeOverMode.enabled": true,
"[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
```
```json
// .vscode/extensions.json
{
"recommendations": [
"vue.volar",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"stylelint.vscode-stylelint",
"bradlc.vscode-tailwindcss",
"eamodio.gitlens"
]
}
```
### 7.2 Git配置
```bash
# .gitignore
node_modules
dist
dist-ssr
*.local
.vscode/*
!.vscode/extensions.json
!.vscode/settings.json
.DS_Store
*.log
coverage
.nyc_output
.env.local
.env.*.local
```
### 7.3 NPM脚本
```json
// package.json
{
"scripts": {
"dev": "vite",
"build": "vue-tsc && vite build",
"build:staging": "vue-tsc && vite build --mode staging",
"preview": "vite preview",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
"lint:style": "stylelint \"src/**/*.{css,scss,vue}\" --fix",
"format": "prettier --write src/",
"format:check": "prettier --check src/",
"test": "vitest",
"test:unit": "vitest run",
"test:coverage": "vitest run --coverage",
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui",
"test:e2e:headed": "playwright test --headed",
"type-check": "vue-tsc --noEmit",
"prepare": "husky install"
}
}
```
---
## 八、最佳实践
### 8.1 依赖管理
#### 8.1.1 依赖版本管理
```json
// package.json
{
"dependencies": {
"vue": "^3.4.0",
"vue-router": "^4.2.0",
"pinia": "^2.1.0"
},
"devDependencies": {
"vite": "^5.0.0",
"typescript": "^5.0.0",
"eslint": "^8.56.0"
}
}
```
#### 8.1.2 依赖安全检查
```bash
# 检查依赖漏洞
npm audit
# 自动修复依赖漏洞
npm audit fix
# 强制修复依赖漏洞
npm audit fix --force
```
### 8.2 性能监控
#### 8.2.1 构建分析
```bash
# 生成构建分析报告
npm run build
# 查看分析报告
open stats.html
```
#### 8.2.2 Bundle大小优化
```typescript
// vite.config.ts
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
'vue-vendor': ['vue', 'vue-router', 'pinia'],
'element-plus': ['element-plus'],
'utils': ['lodash-es', 'dayjs']
}
}
},
chunkSizeWarningLimit: 500
}
})
```
### 8.3 文档管理
#### 8.3.1 README文档
```markdown
# 健身房管理系统前端
## 项目介绍
健身房管理系统前端项目,基于Vue3 + Vite + TypeScript构建。
## 技术栈
- Vue 3.4+
- TypeScript 5.0+
- Vite 5.0+
- Pinia 2.1+
- Element Plus 2.5+
## 快速开始
### 安装依赖
\`\`\`bash
npm install
\`\`\`
### 开发
\`\`\`bash
npm run dev
\`\`\`
### 构建
\`\`\`bash
npm run build
\`\`\`
### 测试
\`\`\`bash
npm run test
\`\`\`
## 项目结构
\`\`\`
src/
├── api/ # API接口
├── assets/ # 静态资源
├── components/ # 组件
├── composables/ # Composables
├── config/ # 配置
├── router/ # 路由
├── stores/ # 状态管理
├── types/ # 类型定义
├── utils/ # 工具函数
└── views/ # 页面
\`\`\`
## 开发规范
详见 [前端开发规范](./docs/design/前端开发规范.md)
## 许可证
MIT
```
#### 8.3.2 CHANGELOG文档
```markdown
# Changelog
## [1.0.0] - 2026-03-04
### Added
- 会员管理功能
- 课程预约功能
- 扫码签到功能
- 数据统计功能
### Changed
- 升级Vue到3.4版本
- 优化构建配置
### Fixed
- 修复预约时间冲突问题
- 修复签到记录显示问题
### Security
- 添加XSS防护
- 添加CSRF防护
```
---
## 九、总结
本文档详细描述了健身房管理系统前端的工程化建设,包括:
1. **工程化概述**:工程化目标、工程化体系
2. **构建工具配置**Vite配置、TypeScript配置、环境变量配置
3. **代码规范工具**ESLint配置、Prettier配置、Stylelint配置
4. **自动化工具**Husky配置、Lint-staged配置、Commitlint配置
5. **CI/CD流程**GitHub Actions配置、CD配置
6. **项目脚手架**:项目初始化、目录结构初始化、基础文件创建
7. **开发工具链**VSCode配置、Git配置、NPM脚本
8. **最佳实践**:依赖管理、性能监控、文档管理
通过遵循本文档的工程化建设指南,可以建立完善的前端工程化体系,提高开发效率、保证代码质量、简化部署流程。