一、前期准备:需求分析与技术选型(1-2天)
核心目标
明确项目场景、团队规模、技术栈偏好,避免盲目选型导致后期重构。
步骤1:需求与约束梳理
| 梳理维度 | 关键问题 | 决策影响 |
|---|---|---|
| 项目类型 | 中后台系统/移动端H5/跨端应用(小程序/App) | 影响框架、构建工具、适配方案选型 |
| 团队规模 | 1-3人小团队/10人以上中大型团队 | 小团队优先"轻量基建",中大型团队需完善规范与协作工具 |
| 兼容性要求 | 需支持IE11/仅支持现代浏览器 | 决定是否需要polyfill、构建工具的兼容配置 |
| 迭代频率 | 快速迭代(每周1-2版)/稳定迭代(每月1版) | 影响CI/CD自动化程度、测试策略 |
步骤2:核心技术栈选型(推荐组合)
| 技术模块 | 主流选型 | 选型建议 |
|---|---|---|
| 框架 | Vue3 + TypeScript / React + TypeScript | 中后台优先Vue3(Element Plus生态成熟),移动端两者均可 |
| 构建工具 | Vite 5.x | 现代项目首选(开发热更新快、配置简洁),复杂老项目可选Webpack 5.x |
| 包管理器 | pnpm 9.x | 速度快、磁盘占用少,兼容npm/yarn命令,支持monorepo(多包管理) |
| 路由 | Vue Router 4.x / React Router 6.x | 与框架强绑定,优先官方推荐版本 |
| 状态管理 | Pinia(Vue3)/ Redux Toolkit(React) | 小项目可不用,中大型项目需统一状态管理 |
| UI组件库 | Element Plus(Vue3)/ Ant Design(React) | 优先选与框架匹配、文档完善、维护活跃的库 |
| 接口请求 | Axios 1.x | 统一封装请求层,处理跨域、拦截器等 |
落地注意事项
- 避免"技术堆砌":小团队无需一开始就搭建复杂基建(如monorepo、微前端),先满足核心需求;
- 版本兼容性:选型时需确认各工具版本匹配(如Vue3需搭配Vite 3+,TypeScript需与框架版本兼容);
- 团队共识:选型前与团队成员对齐,确保至少2人熟悉核心工具,降低维护成本。
二、项目初始化:搭建基础工程结构(1天)
步骤1:用脚手架创建项目
bash
# Vue3 + Vite + TypeScript 项目(推荐)
pnpm create vite@latest my-project --template vue-ts
cd my-project
pnpm install
# React + Vite + TypeScript 项目
pnpm create vite@latest my-project --template react-ts
cd my-project
pnpm install
步骤2:规范目录结构(推荐结构)
my-project/
├── src/
│ ├── api/ # 接口请求目录(按业务模块拆分)
│ ├── assets/ # 静态资源(图片、字体、全局样式)
│ ├── components/ # 通用组件(全局组件+业务组件)
│ │ ├── common/ # 全局通用组件(如Button、Input)
│ │ └── business/ # 业务组件(如OrderTable、UserForm)
│ ├── hooks/ # 自定义钩子(如useRequest、usePermission)
│ ├── router/ # 路由配置(含权限守卫)
│ ├── store/ # 状态管理(Pinia/Redux)
│ ├── types/ # TypeScript类型定义(全局类型+接口类型)
│ ├── utils/ # 工具函数(日期、校验、埋点等)
│ ├── views/ # 页面视图(按业务模块拆分)
│ ├── App.vue/tsx # 根组件
│ └── main.ts # 入口文件
├── .env.development # 开发环境配置
├── .env.production # 生产环境配置
├── .eslintrc.js # ESLint配置
├── .prettierrc # Prettier配置
├── vite.config.ts # Vite配置
└── tsconfig.json # TypeScript配置
步骤3:基础配置(vite.config.ts核心配置)
typescript
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import path from 'path';
import { loadEnv } from 'vite';
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd()); // 加载环境变量
return {
// 路径别名(简化导入)
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
// 开发服务器(解决跨域、热更新)
server: {
port: 3000, // 开发端口
open: true, // 启动后自动打开浏览器
proxy: {
// 接口代理(解决跨域)
'/api': {
target: env.VITE_API_BASE_URL, // 后端接口地址(从环境变量读取)
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
// 构建配置(优化产物)
build: {
target: 'es2015', // 兼容目标浏览器
chunkSizeWarningLimit: 1500, // 代码分割阈值(避免大文件警告)
rollupOptions: {
output: {
// 按模块拆分代码(优化加载速度)
manualChunks: {
vue: ['vue', 'vue-router', 'pinia'],
elementPlus: ['element-plus'],
},
},
},
},
plugins: [vue()],
};
});
落地注意事项
-
路径别名必须在
tsconfig.json中同步配置(否则TS会报错):json{ "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["src/*"] } } } -
环境变量命名必须以
VITE_开头(Vite默认暴露该前缀的变量); -
目录结构无需一步到位,但需预留扩展空间(如按业务模块拆分
views和api)。
三、代码规范体系:统一编码风格与提交规范(1天)
步骤1:集成ESLint + Prettier(代码格式校验)
安装依赖
bash
# ESLint(代码逻辑校验)+ Prettier(代码格式化)
pnpm add -D eslint prettier eslint-plugin-vue @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-config-prettier eslint-plugin-prettier
配置文件
.eslintrc.js(核心配置):
javascript
module.exports = {
root: true,
env: {
browser: true,
node: true,
es2021: true,
},
parser: 'vue-eslint-parser', // Vue文件解析器
parserOptions: {
parser: '@typescript-eslint/parser', // TS解析器
ecmaVersion: 'latest',
sourceType: 'module',
},
plugins: ['vue', '@typescript-eslint', 'prettier'],
extends: [
'eslint:recommended',
'plugin:vue/vue3-recommended',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended', // 整合Prettier(优先Prettier规则)
],
rules: {
// 自定义规则(根据团队需求调整)
'vue/multi-word-component-names': 'off', // 允许单字组件名
'@typescript-eslint/no-explicit-any': 'off', // 临时关闭any限制(逐步优化)
'prettier/prettier': ['error', {}, { usePrettierrc: true }],
},
};
.prettierrc(格式化规则):
json
{
"semi": true, // 结尾加分号
"singleQuote": true, // 使用单引号
"tabWidth": 2, // 缩进2个空格
"trailingComma": "es5", // 对象末尾加逗号
"printWidth": 120, // 换行阈值
"arrowParens": "avoid" // 箭头函数单参数省略括号
}
-
.eslintignore(忽略文件):node_modules/
dist/
.env*
*.d.ts
步骤2:集成husky + lint-staged + commitlint(提交校验)
安装依赖
bash
# husky(Git钩子工具)+ lint-staged(暂存区代码校验)
pnpm add -D husky lint-staged
# commitlint(提交信息校验)
pnpm add -D @commitlint/cli @commitlint/config-conventional
初始化配置
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 --no -- commitlint --edit $1"
配置文件
lint-staged.config.js(暂存区校验规则):
javascript
module.exports = {
'*.{vue,ts,js}': ['eslint --fix', 'prettier --write'], // 自动修复并格式化
'*.{css,scss}': ['stylelint --fix', 'prettier --write'], // 后续集成Stylelint可添加
'*.{md,json}': ['prettier --write'],
};
commitlint.config.js(提交信息规则):
javascript
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
// 提交类型限制(feat/feat: 新功能,fix/fix: 修复bug,docs: 文档等)
'type-enum': [
2,
'always',
['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore', 'revert'],
],
'type-case': [0], // 允许类型大小写(如Feat/FIX)
'subject-empty': [2, 'never'], // 提交描述不能为空
},
};
package.json添加脚本
json
{
"scripts": {
"lint": "eslint . --ext .vue,.ts,.js",
"lint:fix": "eslint . --ext .vue,.ts,.js --fix",
"format": "prettier --write \"src/**/*.{vue,ts,js,css,scss}\""
}
}
落地注意事项
- 首次集成时需执行
pnpm run lint:fix修复历史代码格式问题,避免提交时校验失败; - 团队成员需熟悉提交规范(如
feat: 新增用户列表接口),可配合commitizen工具生成提交信息; - 可根据团队习惯调整ESLint/Prettier规则,避免过度严格导致开发效率下降。
四、依赖与模块管理:规范依赖使用与版本控制(0.5天)
步骤1:依赖分类管理
- ** dependencies **:生产环境必需依赖(如vue、axios、element-plus);
- ** devDependencies **:开发环境依赖(如eslint、vite、jest);
- 避免将开发依赖误放入
dependencies,增加包体积。
步骤2:依赖治理工具
- ** 检测未使用依赖 **:
bash
pnpm add -D depcheck
npx depcheck # 扫描项目中未使用的依赖
- ** 检测安全漏洞 **:
bash
pnpm audit # 内置命令,检测依赖漏洞
pnpm audit fix # 自动修复可修复的漏洞
- ** 自动更新依赖 **:
bash
# 安装renovate(需在GitHub/GitLab配置)
# 功能:自动检测依赖更新,生成PR提醒团队更新
步骤3:模块规范统一
- 项目中统一使用
ESM模块规范(import/export),避免CommonJS(require/module.exports); - TypeScript项目需在
tsconfig.json中设置"module": "ESNext",确保模块解析正确。
落地注意事项
- 锁定依赖版本:pnpm会自动生成
pnpm-lock.yaml,提交到Git仓库,确保团队成员安装的依赖版本一致; - 避免依赖过多:小工具优先自己封装(如日期格式化),减少第三方依赖引入,降低维护成本;
- 重大依赖更新(如Vue3从3.2.x升级到3.4.x)需单独测试,避免兼容性问题。
五、测试体系:保障代码质量(1-2天)
步骤1:单元测试 + 组件测试(核心)
安装依赖(Vue3项目示例)
bash
pnpm add -D jest @vue/test-utils@next vue-jest@next @types/jest ts-jest
配置文件(jest.config.js)
javascript
module.exports = {
preset: 'ts-jest',
testEnvironment: 'jsdom', // 模拟浏览器环境
moduleFileExtensions: ['vue', 'ts', 'js'],
transform: {
'^.+\\.vue$': '@vue/vue3-jest', // 解析Vue文件
'^.+\\.ts$': 'ts-jest', // 解析TS文件
},
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1', // 路径别名映射
},
testMatch: ['**/__tests__/**/*.spec.[jt]s?(x)'], // 测试文件匹配规则
coverageReporters: ['text', 'lcov'], // 测试覆盖率报告格式
};
编写测试用例(示例:components/common/Button.spec.ts)
typescript
import { mount } from '@vue/test-utils';
import Button from './Button.vue';
describe('Button组件', () => {
test('渲染正常', () => {
const wrapper = mount(Button, {
props: { type: 'primary' },
slots: { default: '测试按钮' },
});
expect(wrapper.text()).toBe('测试按钮');
expect(wrapper.classes()).toContain('el-button--primary');
});
test('点击事件触发', async () => {
const mockClick = jest.fn();
const wrapper = mount(Button, {
props: { onClick: mockClick },
});
await wrapper.trigger('click');
expect(mockClick).toHaveBeenCalledTimes(1);
});
});
package.json添加脚本
json
{
"scripts": {
"test": "jest",
"test:coverage": "jest --coverage" // 生成测试覆盖率报告
}
}
步骤2:端到端测试(E2E,可选)
安装依赖(Cypress)
bash
pnpm add -D cypress
配置文件(cypress.config.ts)
typescript
import { defineConfig } from 'cypress';
export default defineConfig({
e2e: {
baseUrl: 'http://localhost:3000', // 测试基础地址
specPattern: 'cypress/e2e/**/*.cy.{js,ts}',
},
});
编写测试用例(cypress/e2e/login.cy.ts)
typescript
describe('登录页面', () => {
it('输入正确账号密码可登录', () => {
cy.visit('/login');
cy.get('input[placeholder="用户名"]').type('admin');
cy.get('input[placeholder="密码"]').type('123456');
cy.get('button[type="submit"]').click();
cy.url().should('include', '/home'); // 验证跳转首页
});
});
package.json添加脚本
json
{
"scripts": {
"cypress:open": "cypress open", // 打开可视化测试工具
"cypress:run": "cypress run" // 命令行运行测试
}
}
落地注意事项
- 小团队可优先覆盖核心组件和工具函数的测试(目标覆盖率≥60%),无需追求100%;
- 测试用例应避免依赖真实后端接口,需配合Mock服务(如MSW)模拟数据;
- 集成到CI/CD流水线(后续步骤),每次提交自动运行测试,避免代码合并后出现问题。
六、接口请求层:统一封装与拦截(0.5天)
步骤1:Axios封装(src/utils/request.ts)
typescript
import axios, { AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios';
import { ElMessage } from 'element-plus';
import { getToken, removeToken } from '@/utils/auth'; // 自定义Token工具
// 创建Axios实例
const service = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL, // 从环境变量读取基础地址
timeout: 10000, // 请求超时时间
});
// 请求拦截器(添加Token、设置请求头)
service.interceptors.request.use(
(config: AxiosRequestConfig) => {
const token = getToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error: AxiosError) => Promise.reject(error)
);
// 响应拦截器(统一处理错误、格式化数据)
service.interceptors.response.use(
(response: AxiosResponse) => {
const res = response.data;
// 假设后端返回格式:{ code: 200, data: {}, msg: '' }
if (res.code !== 200) {
ElMessage.error(res.msg || '请求失败');
// Token过期处理
if (res.code === 401) {
removeToken();
window.location.href = '/login';
}
return Promise.reject(res);
}
return res.data; // 直接返回data,简化业务层调用
},
(error: AxiosError) => {
ElMessage.error(error.message || '网络错误');
return Promise.reject(error);
}
);
export default service;
步骤2:接口管理(按业务模块拆分)
src/api/user.ts(用户模块接口)
typescript
import request from '@/utils/request';
import { LoginParams, UserInfo } from '@/types/user';
// 登录
export const login = (params: LoginParams) => {
return request({
url: '/auth/login',
method: 'post',
data: params,
});
};
// 获取用户信息
export const getUserInfo = () => {
return request({
url: '/user/info',
method: 'get',
});
};
落地注意事项
- 统一错误处理:避免业务层重复写try/catch,拦截器中统一捕获并提示错误;
- 环境变量区分接口地址:开发/测试/生产环境使用不同的
VITE_API_BASE_URL,避免硬编码; - 接口类型定义:用TypeScript定义请求参数和响应数据类型,提升代码提示和健壮性。
七、部署与CI/CD:自动化构建与发布(1天)
步骤1:构建产物优化
1. 静态资源优化(vite.config.ts补充配置)
typescript
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { visualizer } from 'rollup-plugin-visualizer'; // 包体积分析工具
export default defineConfig({
plugins: [
vue(),
visualizer({ open: true }), // 构建后自动打开包体积分析页面
],
build: {
assetsDir: 'static', // 静态资源目录
minify: 'terser', // 启用Terser压缩(默认)
terserOptions: {
compress: {
drop_console: import.meta.env.MODE === 'production', // 生产环境移除console
},
},
},
});
2. 图片优化(安装依赖)
bash
pnpm add -D vite-plugin-imagemin
vite.config.ts添加插件
typescript
import viteImagemin from 'vite-plugin-imagemin';
export default defineConfig({
plugins: [
vue(),
viteImagemin({
gifsicle: { optimizationLevel: 7, interlaced: false },
optipng: { optimizationLevel: 7 },
mozjpeg: { quality: 80 },
pngquant: { quality: [0.8, 0.9], speed: 4 },
}),
],
});
步骤2:CI/CD流水线配置(GitHub Actions示例)
新建.github/workflows/deploy.yml
yaml
name: 前端自动构建与部署
on:
push:
branches: [main] # 监听main分支提交
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
# 1. 拉取代码
- name: 拉取代码
uses: actions/checkout@v4
# 2. 安装Node.js
- name: 安装Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm' # 缓存pnpm依赖
# 3. 安装依赖
- name: 安装依赖
run: pnpm install
# 4. 运行测试
- name: 运行测试
run: pnpm run test
# 5. 构建项目
- name: 构建项目
run: pnpm run build
env:
VITE_API_BASE_URL: ${{ secrets.VITE_API_BASE_URL }} # 从GitHub Secrets读取环境变量
# 6. 部署到阿里云OSS(示例:也可部署到Nginx/Netlify)
- name: 部署到OSS
uses: jakejarvis/s3-sync-action@master
with:
args: --delete # 删除OSS上不存在的文件
env:
AWS_S3_BUCKET: ${{ secrets.OSS_BUCKET }} # OSS桶名
AWS_ACCESS_KEY_ID: ${{ secrets.OSS_ACCESS_KEY }} # 访问密钥
AWS_SECRET_ACCESS_KEY: ${{ secrets.OSS_SECRET_KEY }} # 密钥
AWS_S3_ENDPOINT: ${{ secrets.OSS_ENDPOINT }} # OSS端点
SOURCE_DIR: 'dist' # 构建产物目录
步骤3:环境配置(.env文件)
.env.development(开发环境)
VITE_API_BASE_URL=http://localhost:8080/api
VITE_ENV=development
.env.production(生产环境)
VITE_API_BASE_URL=https://api.example.com
VITE_ENV=production
落地注意事项
- 敏感信息存储:避免在代码中硬编码密钥、接口地址,使用GitHub Secrets或环境变量;
- 部署前测试:CI/CD中必须包含测试步骤,测试失败则终止部署;
- 构建产物备份:每次部署前备份历史产物,便于回滚(如部署失败可快速恢复上一版本)。
八、监控与运维:问题排查与性能优化(1天)
步骤1:错误监控(集成Sentry)
安装依赖
bash
pnpm add @sentry/vue @sentry/tracing
初始化(src/main.ts)
typescript
import { createApp } from 'vue';
import { createRouter } from 'vue-router';
import * as Sentry from '@sentry/vue';
import { Integrations } from '@sentry/tracing';
import App from './App.vue';
import router from './router';
const app = createApp(App);
// 仅生产环境启用Sentry
if (import.meta.env.PROD) {
Sentry.init({
app,
dsn: '你的Sentry DSN地址', // 从Sentry后台获取
integrations: [
new Integrations.BrowserTracing({
routingInstrumentation: Sentry.vueRouterInstrumentation(router),
tracePropagationTargets: ['api.example.com', 'localhost'],
}),
],
tracesSampleRate: 1.0, // 采样率(生产环境可设为0.1,减少性能影响)
});
}
app.use(router).mount('#app');
步骤2:性能监控(集成Web Vitals)
安装依赖
bash
pnpm add web-vitals
封装监控函数(src/utils/performance.ts)
typescript
import { getLCP, getFID, getCLS, getFCP, getTTFB } from 'web-vitals';
// 上报性能数据到后端
const reportPerformance = (metric: any) => {
const body = {
name: metric.name, // 指标名称(LCP/FID等)
value: metric.value, // 指标值
rating: metric.rating, // 评级(good/needs-improvement/poor)
delta: metric.delta, // 与上次测量的差值
page: window.location.pathname,
timestamp: Date.now(),
};
// 异步上报(避免影响页面加载)
navigator.sendBeacon('/api/performance/report', JSON.stringify(body));
};
// 初始化性能监控
export const initPerformanceMonitor = () => {
if (import.meta.env.PROD) {
getLCP(reportPerformance);
getFID(reportPerformance);
getCLS(reportPerformance);
getFCP(reportPerformance);
getTTFB(reportPerformance);
}
};
在入口文件调用(src/main.ts)
typescript
import { initPerformanceMonitor } from '@/utils/performance';
app.mount('#app');
initPerformanceMonitor();
步骤3:埋点系统(业务数据采集)
封装埋点工具(src/utils/track.ts)
typescript
// 埋点上报函数
export const trackEvent = (eventName: string, data?: Record<string, any>) => {
if (import.meta.env.PROD) {
// 1. 浏览器埋点(通过图片请求上报)
const img = new Image();
const params = new URLSearchParams({
event: eventName,
data: JSON.stringify(data || {}),
url: window.location.href,
timestamp: Date.now().toString(),
uuid: localStorage.getItem('userUuid') || '',
});
img.src = `https://track.example.com/log?${params}`;
// 2. 或集成第三方埋点(如百度统计、友盟)
// window._hmt?.push(['_trackEvent', eventName, JSON.stringify(data)]);
}
};
业务中使用
vue
<!-- 按钮点击埋点示例 -->
<template>
<button @click="handleClick">提交订单</button>
</template>
<script setup>
import { trackEvent } from '@/utils/track';
const handleClick = () => {
// 上报"提交订单"事件,携带订单ID
trackEvent('order_submit', { orderId: '123456' });
// 其他业务逻辑
};
</script>
落地注意事项
- 监控工具仅在生产环境启用,避免影响开发/测试环境性能;
- 埋点需遵循用户隐私协议,不采集敏感信息(如手机号、密码);
- 定期分析监控数据:重点关注错误率高的页面、性能差的接口,优先优化核心路径。
九、落地与迭代:分阶段推进与团队适配(持续)
阶段1:核心基建搭建(1周内)
- 完成项目初始化、工程化构建、代码规范、接口封装;
- 团队成员熟悉工具使用(如ESLint修复、Git提交规范);
- 上线第一个测试版本,验证基建可用性。
阶段2:完善与优化(2-4周)
- 补充测试体系、CI/CD流水线、监控系统;
- 根据团队反馈调整规范(如ESLint规则、目录结构);
- 优化构建产物体积、开发体验(如Mock服务集成)。
阶段3:长期维护与迭代(持续)
- 定期更新依赖版本(如Vite、框架版本),修复安全漏洞;
- 沉淀业务工具库和组件库,提升复用率;
- 接入新的基建工具(如微前端、Module Federation),适配项目规模增长。
团队适配注意事项
- 编写基建文档:整理工具使用手册、配置说明,方便新成员上手;
- 组织内部培训:针对核心工具(如Vite配置、Jest测试)进行1-2次分享;
- 设立基建负责人:专人维护基建配置,避免多人修改导致混乱。