# 前端代码质量保障体系
# 目录
# 引言
在现代前端开发中,随着项目规模和团队规模的不断扩大,代码质量问题变得日益突出。一个完善的代码质量保障体系不仅能够减少线上问题,提高开发效率,还能降低维护成本,改善开发体验。本文将详细介绍如何构建一套完整的前端代码质量保障流程,帮助团队持续交付高质量的代码。
# 代码质量度量指标
# 关键质量指标
可读性与可维护性
- 代码复杂度 (圈复杂度)
- 函数/文件长度
- 代码注释率
- 命名规范符合度
可靠性
- 静态分析问题数
- 单元测试覆盖率
- 关键路径测试覆盖率
- 已知bug数量和严重程度
性能指标
- 首次加载时间
- 交互响应时间
- 资源体积
- 内存使用情况
安全指标
- 已知漏洞数量
- 依赖包安全问题数量
- 安全最佳实践符合度
# 指标收集与计算
// sonarqube-scanner配置示例
const scanner = require('sonarqube-scanner');
scanner(
{
serverUrl: 'http://sonarqube-server:9000',
token: process.env.SONAR_TOKEN,
options: {
'sonar.projectName': '前端项目',
'sonar.projectKey': 'frontend-project',
'sonar.sources': 'src',
'sonar.tests': 'tests',
'sonar.javascript.lcov.reportPaths': 'coverage/lcov.info',
'sonar.testExecutionReportPaths': 'test-report.xml',
'sonar.javascript.file.suffixes': '.js,.jsx,.ts,.tsx',
'sonar.exclusions': '**/node_modules/**,**/*.test.js'
}
},
() => {
console.log('SonarQube 分析完成');
}
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 代码规范与风格统一
# 编码规范定义
编码规范是保障代码质量的第一道防线,一个好的编码规范应该包括:
- 命名约定
- 变量/函数/类命名规则
- 文件命名规则
- 常量命名规则
- 代码格式
- 缩进与空格
- 行长度限制
- 括号和空行规则
- 语言特性使用
- ES6+特性使用规范
- 条件语句规范
- 错误处理规范
- 注释规范
-JSDoc风格注释
- 代码块和函数注释要求
- 弃用和TODO标记规范
# 工具配置
# ESLint配置
// .eslintrc.js
module.exports = {
root: true,
env: {
browser: true,
node: true,
es2021: true
},
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended'
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true
},
ecmaVersion: 12,
sourceType: 'module'
},
plugins: [
'react',
'@typescript-eslint',
'react-hooks',
'prettier',
'import',
'jsx-a11y'
],
rules: {
// 错误预防规则
'no-console': ['warn', { allow: ['warn', 'error'] }],
'no-debugger': 'warn',
'no-unused-vars': 'off', // 使用TypeScript的规则
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',
// 代码风格规则
'prettier/prettier': 'error',
'react/prop-types': 'off', // 使用TypeScript类型
'react/react-in-jsx-scope': 'off', // 适用于React 17+
// 可访问性规则
'jsx-a11y/alt-text': 'warn',
'jsx-a11y/anchor-has-content': 'warn',
// 导入规则
'import/order': [
'error',
{
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
'newlines-between': 'always',
alphabetize: { order: 'asc', caseInsensitive: true }
}
]
},
settings: {
react: {
version: 'detect'
}
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# Prettier配置
// .prettierrc.js
module.exports = {
printWidth: 100,
tabWidth: 2,
useTabs: false,
semi: true,
singleQuote: true,
quoteProps: 'as-needed',
jsxSingleQuote: false,
trailingComma: 'es5',
bracketSpacing: true,
jsxBracketSameLine: false,
arrowParens: 'avoid',
rangeStart: 0,
rangeEnd: Infinity,
requirePragma: false,
insertPragma: false,
proseWrap: 'preserve',
htmlWhitespaceSensitivity: 'css',
endOfLine: 'lf'
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# StyleLint配置(CSS样式规范)
// .stylelintrc.js
module.exports = {
extends: [
'stylelint-config-standard',
'stylelint-config-recommended-scss',
'stylelint-config-prettier'
],
plugins: ['stylelint-scss', 'stylelint-order'],
rules: {
'order/properties-alphabetical-order': true,
'color-hex-case': 'lower',
'color-hex-length': 'short',
'no-empty-source': null,
'selector-pseudo-class-no-unknown': [
true,
{
ignorePseudoClasses: ['global', 'local']
}
]
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 规范文档化与共享
# 前端编码规范
## 命名规范
### 变量命名
- 使用有意义的、可发音的变量名
- 使用驼峰式命名法 (camelCase)
- 布尔类型变量使用"is", "has", "can"等前缀
```javascript
// 不推荐
const u = getUser();
const yyyymmdd = '2023-03-28';
const val = true;
// 推荐
const user = getUser();
const formattedDate = '2023-03-28';
const isValid = true;
```
### 函数命名
- 函数名应使用动词或动词短语
- 使用驼峰式命名法
- 函数名应清晰表明函数的功能
```javascript
// 不推荐
function data() {}
function process(data) {}
// 推荐
function getData() {}
function processUserData(data) {}
```
...更多规范...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 静态代码分析
# 深入ESLint配置
# 自定义规则
// ESLint自定义规则示例
// no-large-objects.js
module.exports = {
meta: {
type: 'suggestion',
docs: {
description: '防止在代码中直接定义大型对象字面量',
category: '最佳实践',
recommended: true
},
schema: [
{
type: 'object',
properties: {
maxProperties: { type: 'number' }
},
additionalProperties: false
}
],
messages: {
tooLarge: '对象包含了{{count}}个属性,超过了最大限制{{max}}。请将其拆分或移至单独文件。'
}
},
create(context) {
const option = context.options[0] || {};
const maxProperties = option.maxProperties || 10;
return {
ObjectExpression(node) {
if (node.properties.length > maxProperties) {
context.report({
node,
messageId: 'tooLarge',
data: {
count: node.properties.length,
max: maxProperties
}
});
}
}
};
}
};
// 在eslint配置中使用
// .eslintrc.js
module.exports = {
// ... 其他配置
plugins: [
// ... 其他插件
'custom-rules'
],
rules: {
// ... 其他规则
'custom-rules/no-large-objects': ['error', { maxProperties: 15 }]
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# 配置 ESLint 插件生态
// 团队共享的ESLint配置
// @company/eslint-config/index.js
module.exports = {
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:@typescript-eslint/recommended',
'plugin:import/errors',
'plugin:import/warnings',
'plugin:import/typescript',
'plugin:jsx-a11y/recommended',
'plugin:sonarjs/recommended', // 检测代码气味
'plugin:security/recommended', // 安全规则检查
'plugin:optimize-regex/recommended', // 正则表达式优化
'plugin:prettier/recommended' // 放在最后
],
plugins: [
'@typescript-eslint',
'react',
'react-hooks',
'import',
'jsx-a11y',
'sonarjs',
'security',
'optimize-regex',
'prettier'
],
// ... 其他配置和规则
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 代码复杂度控制
# ESLint复杂度规则
// .eslintrc.js中的复杂度规则
module.exports = {
// ... 其他配置
rules: {
// ... 其他规则
'complexity': ['error', 10], // 限制圈复杂度不超过10
'max-depth': ['error', 4], // 嵌套块深度不超过4
'max-len': ['error', { code: 100, ignoreComments: true }], // 行长度限制
'max-lines-per-function': ['error', { max: 50, skipBlankLines: true, skipComments: true }], // 函数行数限制
'max-nested-callbacks': ['error', 3], // 回调嵌套深度
'max-params': ['error', 4], // 参数数量限制
'sonarjs/cognitive-complexity': ['error', 15] // SonarJS认知复杂度
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 静态分析集成
# SonarQube/SonarCloud 集成
// sonar-project.properties
sonar.projectKey=my-project
sonar.projectName=My Project
sonar.projectVersion=1.0
sonar.sources=src
sonar.tests=tests
sonar.exclusions=node_modules/**,coverage/**,dist/**
sonar.test.inclusions=**/*.test.js,**/*.spec.js,**/*.test.tsx,**/*.spec.tsx
sonar.javascript.lcov.reportPaths=coverage/lcov.info
sonar.testExecutionReportPaths=test-report.xml
sonar.typescript.tsconfigPath=tsconfig.json
sonar.qualitygate.wait=true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 自定义质量门禁
# .github/workflows/quality-gate.yml
name: Quality Gate
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
quality-gate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Type check
run: npm run type-check
- name: Test with coverage
run: npm run test:coverage
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 类型检查系统
# TypeScript配置优化
// tsconfig.json
{
"compilerOptions": {
// 目标ECMAScript版本
"target": "es2018",
"module": "esnext",
"lib": ["dom", "dom.iterable", "esnext"],
// 模块解析配置
"moduleResolution": "node",
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"],
"@utils/*": ["src/utils/*"]
},
// 严格类型检查
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true,
"noUncheckedIndexedAccess": true,
// 额外检查
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
// JSX支持
"jsx": "react-jsx",
// 构建相关
"allowJs": true,
"declaration": true,
"sourceMap": true,
"outDir": "dist",
"esModuleInterop": true,
"resolveJsonModule": true,
"isolatedModules": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "coverage", "**/*.test.ts", "**/*.spec.ts"]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 类型定义最佳实践
// 1. 使用准确的类型而非any
// 不推荐
function processData(data: any): any {
return data.map(item => item.value);
}
// 推荐
interface DataItem {
id: number;
value: string;
timestamp: Date;
}
function processData(data: DataItem[]): string[] {
return data.map(item => item.value);
}
// 2. 使用类型别名和接口
// 类型别名用于联合类型、交叉类型或工具类型
type ButtonSize = 'small' | 'medium' | 'large';
type ButtonProps = {
label: string;
size: ButtonSize;
onClick: () => void;
} & CommonProps;
// 接口用于对象结构和类实现
interface UserData {
id: string;
name: string;
email: string;
roles: string[];
}
// 3. 函数类型定义
type FetchFunction<T> = (url: string, options?: RequestInit) => Promise<T>;
const fetchUsers: FetchFunction<UserData[]> = async (url, options) => {
const response = await fetch(url, options);
return response.json();
};
// 4. 泛型约束
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
// 5. 字面量类型和常量断言
const config = {
theme: 'dark' as const,
animate: true as const,
timeout: 1000
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# 类型检查工作流集成
// package.json
{
"scripts": {
"type-check": "tsc --noEmit",
"type-check:watch": "tsc --noEmit --watch"
}
}
1
2
3
4
5
6
7
2
3
4
5
6
7
# CI集成示例 (.github/workflows/typescript.yml)
name: TypeScript Check
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
type-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Install dependencies
run: npm ci
- name: Run type check
run: npm run type-check
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 自动化测试策略
# 测试金字塔实践
前端测试金字塔应该包含:
- 单元测试: 测试独立组件和函数
- 集成测试: 测试组件组合
- E2E测试: 测试完整用户流程 比例应该大致为:
- 70% 单元测试
- 20% 集成测试
- 10% E2E测试
# 单元测试最佳实践
// React组件单元测试 - Jest + React Testing Library
import { render, screen, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import Button from './Button';
describe('Button组件', () => {
// 基本渲染测试
test('渲染按钮文本', () => {
render(<Button>点击我</Button>);
expect(screen.getByText('点击我')).toBeInTheDocument();
});
// 交互测试
test('点击按钮调用onClick处理函数', async () => {
const handleClick = jest.fn();
render(<Button onClick={handleClick}>点击我</Button>);
await userEvent.click(screen.getByText('点击我'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
// 属性测试
test('禁用状态下按钮不可点击', async () => {
const handleClick = jest.fn();
render(<Button disabled onClick={handleClick}>点击我</Button>);
const button = screen.getByText('点击我');
expect(button).toBeDisabled();
await userEvent.click(button);
expect(handleClick).not.toHaveBeenCalled();
});
// 快照测试
test('按钮样式匹配快照', () => {
const { container } = render(<Button variant="primary">点击我</Button>);
expect(container.firstChild).toMatchSnapshot();
});
});
// 工具函数测试
import { formatCurrency } from '../utils/formatters';
describe('formatCurrency函数', () => {
test('正确格式化整数金额', () => {
expect(formatCurrency(1000, 'CNY')).toBe('¥1,000.00');
});
test('正确格式化小数金额', () => {
expect(formatCurrency(1000.5, 'CNY')).toBe('¥1,000.50');
});
test('处理负数金额', () => {
expect(formatCurrency(-1000, 'CNY')).toBe('-¥1,000.00');
});
test('处理不同货币符号', () => {
expect(formatCurrency(1000, 'USD')).toBe('$1,000.00');
expect(formatCurrency(1000, 'EUR')).toBe('€1,000.00');
});
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# 集成测试策略
// 组件集成测试示例 - 测试表单和API交互
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { rest } from 'msw';
import { setupServer } from 'msw/node';
import LoginForm from './LoginForm';
// 模拟API服务器
const server = setupServer(
rest.post('/api/login', (req, res, ctx) => {
const { username, password } = req.body;
if (username === 'testuser' && password === 'password123') {
return res(
ctx.status(200),
ctx.json({
token: 'fake-token-123',
user: { id: 1, name: 'Test User' }
})
);
} else {
return res(
ctx.status(401),
ctx.json({ message: '用户名或密码错误' })
);
}
})
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
describe('LoginForm组件集成测试', () => {
test('提交正确的凭据时成功登录', async () => {
const onLoginSuccess = jest.fn();
render(<LoginForm onLoginSuccess={onLoginSuccess} />);
// 填写表单
await userEvent.type(screen.getByLabelText(/用户名/i), 'testuser');
await userEvent.type(screen.getByLabelText(/密码/i), 'password123');
await userEvent.click(screen.getByRole('button', { name: /登录/i }));
// 等待API响应
await waitFor(() => {
expect(onLoginSuccess).toHaveBeenCalledWith({
token: 'fake-token-123',
user: { id: 1, name: 'Test User' }
});
});
// 检查表单状态
expect(screen.queryByText(/加载中/i)).not.toBeInTheDocument();
expect(screen.queryByText(/错误/i)).not.toBeInTheDocument();
});
test('提交错误的凭据时显示错误信息', async () => {
render(<LoginForm onLoginSuccess={jest.fn()} />);
// 填写错误的凭据
await userEvent.type(screen.getByLabelText(/用户名/i), 'wronguser');
await userEvent.type(screen.getByLabelText(/密码/i), 'wrongpass');
await userEvent.click(screen.getByRole('button', { name: /登录/i }));
// 等待错误消息
await waitFor(() => {
expect(screen.getByText(/用户名或密码错误/i)).toBeInTheDocument();
});
});
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# E2E测试配置
// Cypress测试示例
// cypress/e2e/login.cy.js
describe('登录流程', () => {
beforeEach(() => {
// 拦截API请求
cy.intercept('POST', '/api/login', (req) => {
const { username, password } = req.body;
if (username === 'testuser' && password === 'password123') {
req.reply({
statusCode: 200,
body: {
token: 'fake-token-123',
user: { id: 1, name: 'Test User' }
}
});
} else {
req.reply({
statusCode: 401,
body: { message: '用户名或密码错误' }
});
}
}).as('loginRequest');
cy.visit('/login');
});
it('成功登录并重定向到仪表板', () => {
cy.get('input[name="username"]').type('testuser');
cy.get('input[name="password"]').type('password123');
cy.get('button[type="submit"]').click();
cy.wait('@loginRequest');
cy.url().should('include', '/dashboard');
cy.get('[data-testid="user-welcome"]').should('contain', 'Test User');
});
it('登录失败时显示错误消息', () => {
cy.get('input[name="username"]').type('wronguser');
cy.get('input[name="password"]').type('wrongpass');
cy.get('button[type="submit"]').click();
cy.wait('@loginRequest');
cy.get('[data-testid="error-message"]').should('be.visible');
cy.get('[data-testid="error-message"]').should('contain', '用户名或密码错误');
cy.url().should('include', '/login');
});
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// cypress.config.js
const { defineConfig } = require('cypress');
module.exports = defineConfig({
e2e: {
baseUrl: 'http://localhost:3000',
viewportWidth: 1280,
viewportHeight: 720,
video: false,
screenshotOnRunFailure: true,
setupNodeEvents(on, config) {
// 实现node事件监听器
},
},
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 测试覆盖率设置
// jest.config.js
module.exports = {
// 其他Jest配置...
collectCoverage: true,
coverageDirectory: 'coverage',
collectCoverageFrom: [
'src/**/*.{js,jsx,ts,tsx}',
'!src/**/*.test.{js,jsx,ts,tsx}',
'!src/**/*.stories.{js,jsx,ts,tsx}',
'!src/index.{js,jsx,ts,tsx}',
'!src/serviceWorker.{js,jsx,ts,tsx}',
'!src/reportWebVitals.{js,jsx,ts,tsx}',
'!src/setupTests.{js,jsx,ts,tsx}',
'!src/mocks/**/*.{js,jsx,ts,tsx}'
],
coverageThreshold: {
global: {
statements: 80,
branches: 75,
functions: 80,
lines: 80
}
},
coverageReporters: ['lcov', 'text', 'html']
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 代码评审机制
# 代码评审清单
# 代码评审清单
## 功能性
- [ ] 代码实现是否符合需求规格
- [ ] 是否处理了边缘情况
- [ ] 是否有适当的错误处理
- [ ] 代码是否处理了安全问题(XSS、CSRF等)
## 可维护性
- [ ] 命名是否清晰直观
- [ ] 代码结构是否简洁合理
- [ ] 是否消除了重复代码
- [ ] 复杂逻辑是否有注释说明
## 性能
- [ ] 是否存在潜在的性能问题
- [ ] 是否有不必要的计算或渲染
- [ ] 是否有合适的缓存策略
## 可测试性
- [ ] 代码是否易于测试
- [ ] 是否有适当的单元测试
- [ ] 测试是否覆盖了关键路径
## 团队约定
- [ ] 是否遵循了团队的代码规范
- [ ] 是否符合项目的架构设计
- [ ] 是否有符合规范的提交信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# PR 模板配置
<!-- .github/PULL_REQUEST_TEMPLATE.md -->
## 变更说明
<!-- 请描述此PR解决的问题或实现的功能 -->
## 变更类型
- [ ] 功能新增
- [ ] Bug修复
- [ ] 代码重构
- [ ] 样式变更
- [ ] 性能优化
- [ ] 测试相关
- [ ] 构建相关
- [ ] 文档更新
- [ ] 其他
## 自测清单
- [ ] 本地测试通过
- [ ] 单元测试已添加/更新
- [ ] 文档已更新
- [ ] 代码遵循规范
## 附加信息
<!-- 任何其他信息,如截图、相关链接等 -->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 自动化评审工具配置
# .github/workflows/code-review.yml
name: Code Review
on:
pull_request:
types: [opened, synchronize]
jobs:
code-review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Type check
run: npm run type-check
- name: Test
run: npm test
- name: Codacy Static Analysis
uses: codacy/codacy-analysis-cli-action@master
with:
project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
upload: true
format: sarif
output: codacy-results.sarif
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 持续集成与部署
# 构建CI/CD流水线
# .github/workflows/ci-cd.yml
name: CI/CD Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
# 质量检查
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '16'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Type check
run: npm run type-check
- name: Test
run: npm run test:coverage
- name: Upload coverage
uses: actions/upload-artifact@v3
with:
name: coverage-report
path: coverage/
# 构建
build:
needs: quality
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '16'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Upload build
uses: actions/upload-artifact@v3
with:
name: build-output
path: dist/
# 部署(仅主分支)
deploy:
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Download build artifacts
uses: actions/download-artifact@v3
with:
name: build-output
path: dist
- name: Deploy to Production
uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: '${{ secrets.GITHUB_TOKEN }}'
firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT }}'
channelId: live
projectId: my-app-12345
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# 环境隔离策略
# 环境配置文件 - firebase.json
{
"hosting": [
{
"target": "production",
"public": "dist",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
],
"headers": [
{
"source": "**/*.@(js|css)",
"headers": [
{
"key": "Cache-Control",
"value": "max-age=31536000"
}
]
}
]
},
{
"target": "staging",
"public": "dist",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
},
{
"target": "development",
"public": "dist",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
]
}
# 环境部署工作流 - .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main, develop, feature/*]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '16'
- name: Install dependencies
run: npm ci
- name: Set environment variables
run: |
if [[ $GITHUB_REF == refs/heads/main ]]; then
echo "DEPLOY_ENV=production" >> $GITHUB_ENV
echo "NODE_ENV=production" >> $GITHUB_ENV
elif [[ $GITHUB_REF == refs/heads/develop ]]; then
echo "DEPLOY_ENV=staging" >> $GITHUB_ENV
echo "NODE_ENV=production" >> $GITHUB_ENV
else
echo "DEPLOY_ENV=development" >> $GITHUB_ENV
echo "NODE_ENV=development" >> $GITHUB_ENV
fi
- name: Build
run: |
npm run build:$DEPLOY_ENV
- name: Deploy to Firebase
uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: '${{ secrets.GITHUB_TOKEN }}'
firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT }}'
projectId: my-project-id
channelId: ${{ env.DEPLOY_ENV }}
target: ${{ env.DEPLOY_ENV }}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# 版本控制与发布策略
// .releaserc.js - Semantic Release配置
module.exports = {
branches: ['main'],
plugins: [
'@semantic-release/commit-analyzer',
'@semantic-release/release-notes-generator',
'@semantic-release/changelog',
'@semantic-release/npm',
['@semantic-release/git', {
assets: ['package.json', 'CHANGELOG.md'],
message: 'chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}'
}],
'@semantic-release/github'
]
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 提交消息规范
我们使用[约定式提交](https://www.conventionalcommits.org/)格式来规范化提交消息,这有助于自动化版本管理和变更日志生成。
## 提交消息格式
1
2
3
4
5
6
2
3
4
5
6
<类型>[可选范围]: <描述>
[可选正文]
[可选页脚]
## 提交类型
- `feat`: 新功能
- `fix`: 修复Bug
- `docs`: 仅文档变更
- `style`: 不影响代码含义的变更(空格、格式化、缺少分号等)
- `refactor`: 既不修复bug也不添加特性的代码更改
- `perf`: 改进性能的代码更改
- `test`: 添加或修正测试
- `build`: 影响构建系统或外部依赖的更改
- `ci`: 对CI配置文件和脚本的更改
- `chore`: 其他不修改src或test文件的更改
- `revert`: 恢复先前的提交
## 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
feat(auth): 添加用户注册功能
实现了用户注册表单和后端API集成
Resolves: #123
1
fix(dashboard): 修复数据加载时的无限循环问题
修复了effect依赖数组缺失导致的问题
Fixes: #456
# 工作流自动化
# Git Hooks 配置
// package.json
{
"scripts": {
"prepare": "husky install"
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"],
".{css,scss,less}": ["prettier --write"],
"*.{json,yaml,yml}": ["prettier --write"]
}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 自动化工作流最佳实践
高效的工作流自动化不仅能提高开发效率,还能保证代码质量。以下是一些最佳实践:
# 1. 保持Git提交干净规范
# .husky/pre-commit
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
# 运行lint-staged进行代码检查和格式化
npx lint-staged
# 检查禁止提交的内容
npx commitlint --edit
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 2. 一键式开发环境设置
创建开发环境初始化脚本,确保团队成员统一环境配置:
// scripts/setup-dev.js
const { execSync } = require('child_process');
const fs = require('fs');
// 检查和安装依赖
console.log('Installing dependencies...');
execSync('npm install', { stdio: 'inherit' });
// 配置环境文件
if (!fs.existsSync('.env.local')) {
console.log('Creating local environment file...');
fs.copyFileSync('.env.example', '.env.local');
}
// 初始化husky
console.log('Setting up Git hooks...');
execSync('npm run prepare', { stdio: 'inherit' });
// 构建依赖项
console.log('Building dependencies...');
execSync('npm run build:deps', { stdio: 'inherit' });
console.log('Development environment setup complete!');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 3. 规范化分支管理
在团队项目中实施标准化的分支模型:
# .husky/pre-push
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
BRANCH=$(git rev-parse --abbrev-ref HEAD)
PROTECTED_BRANCHES="^(main|master|develop)$"
if [[ $BRANCH =~ $PROTECTED_BRANCHES ]]; then
echo "Direct push to $BRANCH is not allowed. Please create a pull request instead."
exit 1
fi
npm test
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 4. 自动化版本和发布管理
利用标准版本控制工具自动化版本号管理和发布流程:
# package.json
{
"scripts": {
"release": "standard-version",
"release:major": "standard-version --release-as major",
"release:minor": "standard-version --release-as minor",
"release:patch": "standard-version --release-as patch"
}
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 性能与安全检测
# 性能检测集成
将性能检测纳入开发流程,及早发现性能问题:
# 1. Lighthouse CI配置
// lighthouserc.js
module.exports = {
ci: {
collect: {
url: ['http://localhost:3000/', 'http://localhost:3000/profile'],
numberOfRuns: 3,
staticDistDir: './build'
},
upload: {
target: 'temporary-public-storage',
},
assert: {
preset: 'lighthouse:recommended',
assertions: {
'first-contentful-paint': ['warn', {maxNumericValue: 2000}],
'interactive': ['error', {maxNumericValue: 3500}],
'largest-contentful-paint': ['error', {maxNumericValue: 2500}],
'cumulative-layout-shift': ['error', {maxNumericValue: 0.1}]
}
},
},
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 2. 性能预算实施
// webpack.config.js (部分)
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
// 测量构建性能
const smp = new SpeedMeasurePlugin();
module.exports = smp.wrap({
// 其他配置...
performance: {
hints: 'warning',
maxEntrypointSize: 250000, // 入口文件大小限制
maxAssetSize: 450000, // 单个资源大小限制
},
plugins: [
// 仅在分析模式下启用
process.env.ANALYZE && new BundleAnalyzerPlugin(),
].filter(Boolean)
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 安全检测工具集成
保障前端应用安全性的集成方案:
# 1. 依赖安全审计
# package.json
{
"scripts": {
"security:audit": "npm audit",
"security:audit:fix": "npm audit fix",
"security:audit:advanced": "snyk test",
"security:audit:ci": "npm audit --audit-level=high"
}
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 2. OWASP ZAP集成
# .github/workflows/security.yml
name: Security Scan
on:
schedule:
- cron: '0 1 * * 1' # 每周一运行
workflow_dispatch:
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up environment
run: |
npm ci
npm run build
npm run start:prod &
sleep 10
- name: ZAP Scan
uses: zaproxy/action-baseline@v0.4.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
target: 'http://localhost:3000'
rules_file_name: '.zap/rules.tsv'
cmd_options: '-a'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 3. 内容安全策略实施
// server.js或Next.js配置
const helmet = require('helmet');
app.use(
helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'", 'https://apis.google.com'],
styleSrc: ["'self'", "'unsafe-inline'", 'https://fonts.googleapis.com'],
imgSrc: ["'self'", 'data:', 'https://secure.example.com'],
connectSrc: ["'self'", 'https://api.example.com'],
fontSrc: ["'self'", 'https://fonts.gstatic.com'],
objectSrc: ["'none'"],
mediaSrc: ["'self'"],
frameSrc: ["'none'"],
},
})
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 质量可视化与报告
# 团队仪表盘构建
将质量数据可视化,提高团队对质量的关注:
# 1. SonarQube仪表盘配置
// sonarqube-dashboard.js
const SonarqubeScanner = require('sonarqube-scanner');
SonarqubeScanner({
serverUrl: 'http://localhost:9000',
token: process.env.SONAR_TOKEN,
options: {
'sonar.projectName': '前端项目仪表盘',
'sonar.projectKey': 'frontend-dashboard',
'sonar.projectVersion': '1.0.0',
'sonar.sources': 'src',
'sonar.tests': 'src',
'sonar.test.inclusions': '**/*.test.js,**/*.test.ts,**/*.test.tsx',
'sonar.javascript.lcov.reportPaths': 'coverage/lcov.info',
'sonar.testExecutionReportPaths': 'test-report.xml',
'sonar.qualitygate.wait': true, // 等待质量门禁结果
// 代码重复检测配置
'sonar.cpd.javascript.minimumTokens': 100,
// 可复用组件检测
'sonar.typescript.tsconfigPath': 'tsconfig.json'
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 2. 生成综合质量报告
// scripts/generate-quality-report.js
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
// 收集测试覆盖率
console.log('收集测试覆盖率...');
execSync('npm run test:coverage', { stdio: 'inherit' });
// 收集代码复杂度指标
console.log('分析代码复杂度...');
execSync('npx eslint --ext .js,.jsx,.ts,.tsx src/ -f json > eslint-report.json', { stdio: 'inherit' });
// 收集Bundle分析
console.log('分析打包体积...');
execSync('ANALYZE=true npm run build', { stdio: 'inherit' });
// 生成综合报告
console.log('生成综合质量报告...');
const testResults = JSON.parse(fs.readFileSync(path.resolve('coverage/coverage-summary.json'), 'utf8'));
const lintResults = JSON.parse(fs.readFileSync(path.resolve('eslint-report.json'), 'utf8'));
const report = {
date: new Date().toISOString(),
coverage: {
statements: testResults.total.statements.pct,
branches: testResults.total.branches.pct,
functions: testResults.total.functions.pct,
lines: testResults.total.lines.pct
},
lintIssues: {
errors: lintResults.filter(item => item.severity === 2).length,
warnings: lintResults.filter(item => item.severity === 1).length
},
// 这里可以添加其他指标
};
fs.writeFileSync(path.resolve('quality-report.json'), JSON.stringify(report, null, 2));
console.log('报告已生成: quality-report.json');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 质量趋势分析
通过持续监测代码质量趋势,发现潜在问题:
// scripts/quality-trend.js
const fs = require('fs');
const path = require('path');
// 读取历史报告
const reportDir = path.resolve('quality-reports');
if (!fs.existsSync(reportDir)) {
fs.mkdirSync(reportDir);
}
// 当前报告
const currentReport = JSON.parse(fs.readFileSync(path.resolve('quality-report.json'), 'utf8'));
// 保存历史数据
const reportFile = path.join(reportDir, `${new Date().toISOString().split('T')[0]}.json`);
fs.writeFileSync(reportFile, JSON.stringify(currentReport, null, 2));
// 分析趋势
const files = fs.readdirSync(reportDir).filter(file => file.endsWith('.json')).sort();
const trend = files.map(file => {
const data = JSON.parse(fs.readFileSync(path.join(reportDir, file), 'utf8'));
return {
date: file.replace('.json', ''),
coverage: data.coverage.statements,
errors: data.lintIssues.errors,
warnings: data.lintIssues.warnings
};
});
console.log('质量趋势分析:');
console.table(trend);
// 检测显著变化
if (trend.length > 1) {
const latest = trend[trend.length - 1];
const previous = trend[trend.length - 2];
if (latest.coverage < previous.coverage - 5) {
console.warn('警告: 测试覆盖率显著下降!');
}
if (latest.errors > previous.errors + 10) {
console.warn('警告: 代码错误数量显著增加!');
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 质量文化建设
# 团队代码质量意识培养
# 1. 质量评估机制
建立定期的代码质量评估和回顾机制:
// 质量考核模板
const qualityEvaluation = {
developer: '开发者名称',
period: '2023-Q3',
metrics: {
codeStyle: {
description: '代码风格符合团队规范',
score: 0, // 1-5分
comments: ''
},
testCoverage: {
description: '编写充分的测试用例',
score: 0,
comments: ''
},
codeReview: {
description: '积极参与代码评审并采纳反馈',
score: 0,
comments: ''
},
documentation: {
description: '提供清晰的文档和注释',
score: 0,
comments: ''
},
bugRate: {
description: '代码引入的缺陷率',
score: 0,
comments: ''
}
},
overallComments: '',
improvementPlan: ''
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 2. 开发最佳实践手册
创建并维护团队专用的开发最佳实践手册:
# 前端开发最佳实践手册
## 1. 编码原则
- 优先可读性和可维护性
- 编写自文档化代码
- 组件设计遵循单一职责原则
## 2. 性能优化指南
- 关键渲染路径优化
- 资源加载策略
- 渲染性能技巧
## 3. 测试策略
- 单元测试覆盖核心业务逻辑
- 组件测试关注用户交互
- 端到端测试保障关键流程
## 4. 安全实践
- 输入验证和消毒
- XSS和CSRF防护
- 敏感信息处理
## 5. 代码评审清单
- 逻辑正确性
- 代码可维护性
- 性能考量
- 安全风险
- 测试覆盖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 持续学习与分享机制
促进团队内部的知识共享和技术提升:
# 1. 技术分享流程
# 技术分享制度
## 分享频率
- 每周一次的团队内部分享
- 每月一次的部门级分享
## 分享主题建议
- 新技术/框架探索
- 解决方案设计
- 疑难问题分析
- 性能优化案例
- 安全实践分享
## 分享流程
1. 提前一周确定主题和分享人
2. 准备分享材料和演示代码
3. 15-30分钟的演讲
4. 5-10分钟的问答环节
5. 分享资料归档到内部知识库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 2. 代码质量精进计划
建立系统性的质量提升计划:
# 代码质量精进计划
## 短期目标(3个月)
- 建立核心组件的单元测试覆盖率达到80%
- 消除所有Critical和Major级别的Lint警告
- 完成主要业务逻辑的TypeScript类型定义
## 中期目标(6个月)
- 建立端到端测试覆盖核心业务流程
- 重构标识出的高复杂度模块
- 优化构建流程,减少30%的构建时间
## 长期目标(12个月)
- 实现持续交付能力,支持多环境自动部署
- 建立性能监控系统,实时检测性能退化
- 培养团队共同维护质量文化
## 实施方法
- 每周质量站会,回顾进度和解决问题
- 建立质量导师制度,促进知识共享
- 质量指标与绩效挂钩,激励质量意识
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
通过以上体系化的质量保障措施,团队可以建立起持续的代码质量改进文化,确保产品的长期健康发展。不仅能减少线上问题和维护成本,还能提升团队的技术实力和工作满意度。