工程化

7. 监控体系

7.1 性能监控

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
// 性能数据上报
class PerformanceMonitor {
constructor() {
this.init();
}

init() {
window.addEventListener('load', () => {
this.reportPerformance();
});
}

reportPerformance() {
const timing = performance.timing;
const metrics = {
// DNS查询时间
dns: timing.domainLookupEnd - timing.domainLookupStart,
// TCP连接时间
tcp: timing.connectEnd - timing.connectStart,
// 白屏时间
blank: timing.domLoading - timing.fetchStart,
// DOM解析时间
dom: timing.domComplete - timing.domLoading,
// 页面加载完成时间
load: timing.loadEventEnd - timing.fetchStart
};

this.report('/api/performance', metrics);
}

report(url, data) {
navigator.sendBeacon(url, JSON.stringify(data));
}
}

new PerformanceMonitor();

7.2 错误监控

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
// 全局错误捕获
class ErrorMonitor {
constructor() {
this.init();
}

init() {
// JS错误
window.addEventListener('error', (event) => {
this.reportError({
type: 'js_error',
message: event.message,
filename: event.filename,
lineno: event.lineno,
colno: event.colno,
stack: event.error?.stack
});
});

// Promise错误
window.addEventListener('unhandledrejection', (event) => {
this.reportError({
type: 'promise_error',
message: event.reason
});
});

// 资源加载错误
window.addEventListener('error', (event) => {
if (event.target !== window) {
this.reportError({
type: 'resource_error',
url: event.target.src || event.target.href
});
}
}, true);
}

reportError(error) {
const data = {
...error,
url: window.location.href,
userAgent: navigator.userAgent,
timestamp: Date.now()
};

navigator.sendBeacon('/api/error', JSON.stringify(data));
}
}

new ErrorMonitor();

7.3 用户行为监控

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
// 埋点SDK
class Analytics {
constructor(appId) {
this.appId = appId;
this.queue = [];
}

// 页面浏览
pageView(pageName) {
this.track('page_view', { page: pageName });
}

// 事件追踪
track(eventName, properties = {}) {
const data = {
appId: this.appId,
event: eventName,
properties,
timestamp: Date.now(),
url: window.location.href,
userId: this.getUserId()
};

this.queue.push(data);
this.flush();
}

// 批量上报
flush() {
if (this.queue.length === 0) return;

const data = [...this.queue];
this.queue = [];

navigator.sendBeacon('/api/analytics', JSON.stringify(data));
}

getUserId() {
return localStorage.getItem('userId') || 'anonymous';
}
}

// 使用
const analytics = new Analytics('my-app-id');
analytics.pageView('home');
analytics.track('button_click', { button: 'login' });

8. 面试口述版本

面试官:请说说你对前端工程化的理解

回答框架:

“前端工程化是通过工具和流程规范,提升开发效率和代码质量的体系。主要包括5个方面:

1. 代码规范

  • ESLint检查代码质量
  • Prettier统一代码格式
  • Git Hooks提交前自动检查
  • Commitlint规范提交信息

2. 构建工具

  • Webpack/Vite打包构建
  • Babel转译ES6+代码
  • 代码分割、Tree Shaking优化
  • 开发服务器、热更新

3. 模块化

  • ES Module标准化模块
  • 支持按需加载、Tree Shaking
  • CommonJS用于Node.js

4. 自动化测试

  • 单元测试(Jest)
  • 集成测试
  • E2E测试(Cypress)
  • 测试覆盖率

5. CI/CD

  • GitHub Actions/GitLab CI自动化部署
  • 代码提交 → 自动测试 → 自动构建 → 自动部署
  • Docker容器化部署

6. 监控体系

  • 性能监控(Performance API)
  • 错误监控(全局捕获)
  • 用户行为分析(埋点)

工程化的目标是提升开发效率、保证代码质量、降低维护成本。”


9. 高频追问

Q1: Webpack和Vite的区别?

Webpack:

  • 基于Bundle,打包所有模块
  • 冷启动慢(需要打包)
  • 热更新慢(需要重新打包)
  • 生态成熟,插件丰富

Vite:

  • 基于ESM,按需加载
  • 冷启动快(无需打包)
  • 热更新快(只更新变化模块)
  • 生产环境使用Rollup打包

选择建议:

  • 新项目:Vite(开发体验好)
  • 老项目:Webpack(生态成熟)

Q2: 如何实现代码规范自动化?

1
2
3
4
5
6
7
1. ESLint + Prettier:代码检查和格式化
2. husky:Git Hooks管理
3. lint-staged:只检查暂存区文件
4. commitlint:提交信息规范

流程:
git commit → pre-commit hook → lint-staged → ESLint/Prettier → 通过才能提交

Q3: CommonJS和ES Module的区别?

特性 CommonJS ES Module
加载时机 运行时 编译时
加载方式 同步 异步
输出 值拷贝 值引用
动态加载 支持 不支持(需import())
Tree Shaking 不支持 支持
使用场景 Node.js 浏览器、现代工具

Q4: CI/CD的完整流程是什么?

1
2
3
4
5
6
7
8
9
10
11
1. 代码提交到Git仓库
2. 触发CI流程:
- 安装依赖
- 代码检查(ESLint)
- 运行测试
- 构建打包
3. 测试通过后触发CD流程:
- 部署到测试环境
- 自动化测试
- 部署到生产环境
4. 监控告警

Q5: 如何做前端监控?

性能监控:

  • Performance API获取性能数据
  • 上报LCP、FID、CLS等指标

错误监控:

  • window.onerror捕获JS错误
  • unhandledrejection捕获Promise错误
  • 资源加载错误监听

用户行为:

  • 页面浏览(PV/UV)
  • 事件追踪(点击、提交)
  • 用户路径分析

上报方式:

  • navigator.sendBeacon(不阻塞)
  • Image对象(兼容性好)
  • fetch/xhr(灵活)

Q6: Monorepo的优缺点?

优点:

  • 代码复用方便
  • 统一依赖管理
  • 原子化提交(多包同时修改)
  • 统一构建、测试、发布

缺点:

  • 仓库体积大
  • 构建时间长
  • 权限管理复杂

工具:

  • pnpm workspace
  • Lerna
  • Nx
  • Turborepo

10. 实战经验

经验1:从0搭建工程化体系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1. 初始化项目
- 选择构建工具(Vite/Webpack)
- 配置TypeScript

2. 代码规范
- 配置ESLint + Prettier
- 配置husky + lint-staged
- 配置commitlint

3. 自动化测试
- 配置Jest单元测试
- 配置Cypress E2E测试
- 设置测试覆盖率要求

4. CI/CD
- 配置GitHub Actions
- 自动化测试、构建、部署

5. 监控体系
- 接入性能监控
- 接入错误监控
- 配置告警规则

经验2:优化构建速度

1
2
3
4
5
6
7
8
9
10
问题:Webpack构建时间从5分钟优化到1分钟

方案:
1. 使用cache缓存
2. 多进程打包(thread-loader)
3. 缩小构建范围(exclude)
4. DllPlugin预编译第三方库
5. 升级到Webpack 5

结果:构建时间从5分钟降到1分钟

经验3:统一团队代码风格

1
2
3
4
5
6
7
8
9
10
问题:团队成员代码风格不统一

方案:
1. 制定ESLint规则
2. 配置Prettier自动格式化
3. Git Hooks强制检查
4. 编辑器配置同步(.editorconfig)
5. Code Review流程

结果:代码风格统一,减少Review时间