跳到主要内容

前端工程化与性能监控面经

前端工程化核心知识

1. Webpack 深度

核心概念

Entry -> Loader 处理 -> Plugin-> Bundle 输出

Loader:转换单个文件(babel-loader / css-loader / file-loader)
Plugin:串联整个构建生命周期(HtmlWebpackPlugin / MiniCssExtractPlugin)
Resolver:模块寻址解析

Tree Shaking 原理

  • 基于 ESM 静态分析模块导入导出
  • Webpack 标记未使用的导出为 unused exports
  • Terser 在压缩时删除未使用代码
  • sideEffects: false 可帮助 Webpack 更激进地删除

Code Splitting

// 1. 入口分割
entry: { main: './src/index.js', vendor: './src/vendor.js' }

// 2. 动态导入
const LazyComp = () => import('./HeavyComponent')

// 3. SplitChunksPlugin 公共依赖提取
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /node_modules/,
name: 'vendors',
chunks: 'all'
}
}
}
}

Webpack vs Vite 对比

对比项WebpackVite
开发启动打包所有模块ESM 按需加载
HMR 速度所有模块出发只有变化的模块
生产构建WebpackRollup
配置复杂度
适用场景复杂大型中小型,新项目

2. Vite 深度

开发模式原理

1. 浏览器请求模块
2. Vite Dev Server 收到请求
3. 对模块应用转换(如 TypeScript 编译)
4. 返回转换后的模块
-> 无需打包,启动速度极快

预构建依赖

  • 第一次启动时用 esbuild 预构建 node_modules
  • esbuild 是 Go 写的,比 Babel 快 10-100x
  • 预构建结果缓存在 node_modules/.vite 中

自定义插件

// vite.config.ts
export default {
plugins: [
{
name: 'my-transform',
transform(code, id) {
if (id.endsWith('.svg')) {
return `export default ${JSON.stringify(code)}`
}
}
}
]
}

3. CI/CD 流水线

# GitHub Actions 示例
name: Deploy
on:
push:
branches: [main]

jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'

- name: Install
run: pnpm install --frozen-lockfile

- name: Lint
run: pnpm lint

- name: Test
run: pnpm test --coverage

- name: Build
run: pnpm build
env:
VITE_API_BASE: ${{ secrets.API_BASE }}

- name: Deploy to CDN
run: pnpm deploy

4. Web 性能监控

Core Web Vitals

指标全称良好阈值优化策略
LCP最大内容绘制< 2.5sSSR、图片优先级、CDN
INP交互到下一帧< 200ms少 JS、Web Worker
CLS累积布局偏移< 0.1图片设宽高、字体预加载

Performance API

// 页面性能采样
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach(entry => {
if (entry.entryType === 'largest-contentful-paint') {
console.log('LCP:', entry.startTime)
}
if (entry.entryType === 'layout-shift') {
console.log('CLS score:', entry.value)
}
})
})
observer.observe({ type: 'largest-contentful-paint', buffered: true })
observer.observe({ type: 'layout-shift', buffered: true })

// Navigation Timing
const timing = performance.timing
const pageLoad = timing.loadEventEnd - timing.navigationStart
const domReady = timing.domContentLoadedEventEnd - timing.navigationStart
const ttfb = timing.responseStart - timing.requestStart

前端监控体系

// 错误监控
window.addEventListener('error', (event) => {
sendToSentry({
message: event.message,
filename: event.filename,
lineno: event.lineno,
colno: event.colno,
error: event.error?.stack
})
})

window.addEventListener('unhandledrejection', (event) => {
sendToSentry({ message: event.reason?.message })
})

// 资源监控
const resourceObserver = new PerformanceObserver((list) => {
list.getEntries().forEach(entry => {
if (entry.duration > 1000) {
// 资源加载过慢
reportSlowResource(entry)
}
})
})
resourceObserver.observe({ type: 'resource', buffered: true })

5. 包体积分析

# Webpack Bundle Analyzer
npx webpack-bundle-analyzer stats.json

# Vite 分析
npx vite-bundle-visualizer

# 常见优化手段
1. 添加 lodash 用 lodash-es 替代
2. moment.js 用 dayjs 替代(体积约 80% 小)
3. echarts 按需导入组件,不要全量导入
4. 图片用 WebP + 懒加载
5. 富文本编辑器就答动态加载

6. 资深面试题

  • Webpack 的 HMR 原理?
    • Dev Server 与浏览器建立 WebSocket
    • 文件变化时,重新编译对应模块
    • 向浏览器推送更新后的模块 hash
    • 浏览器下载新模块,通过 module.hot.accept 回调操作
  • Vite 为什么开发时很快但构建时还用 Rollup?
    • 开发时利用浏览器原生 ESM,无需打包
    • 生产构建需要 Tree Shaking、代码分割、压缩等 Rollup 的能力
    • 未来 Rolldown(Rust 实现)将统一开发和生产
  • 如何实现前端性能监控平台?
    • Performance API 采频 Core Web Vitals
    • 错误监控捕获未处理异常
    • User Timing API 自定义性能埋点
    • 上报到分析平台(自建 + 各种 SaaS)
  • Webpack 构建慢如何优化?
    • cache: { type: 'filesystem' } 文件系统缓存
    • thread-loader 并行处理 Loader
    • babel-loader 配置 include: /src/ 缩小范围
    • 将稳定依赖定义为 外部扩展 externals + CDN