前端工程化篇
面试定位:本章聚焦“全栈(偏 Node)”工程师在 2025 年如何回答工程化与提效题,包括工具选型、自动化、AI 协作与交付经验。
1. 2025 构建工具全景(唤起对比思维)
工具 | 核心卖点 | 适用场景 | 节点亮点 |
---|---|---|---|
Webpack 5 | 成熟生态、细粒度控制 | 老项目增量升级、复杂微前端 | 结合 module federation 、rspack 兼容模式 |
Vite 5 | ESBuild 预构建 + Rollup 生产包 | Vue/React 中后台、BFF 同构 | server.preTransformRequests 可加 AI Mock、ssrLoadModule 支持 Edge 渲染 |
Rspack | Rust + SWC 极致构建 | 大型 ToC、Node 同构 SSR | 与 Rslib 、Farm 同属 Rust 生态,兼容 Webpack 配置 |
Nx/TurboRepo | Monorepo 流水线 | 全栈团队(Node + 移动 + Infra) | 适配 pnpm 、task graph 可调用 AI 生成步骤说明 |
面试提问常见:“为什么项目还在用 Webpack?”建议回答:利用 Webpack 的生态(Module Federation、老插件),并说明迁移策略(例如引入 @module-federation/nextjs-ssr
或逐步替换为 Rspack 以提升构建速度)。
2. Loader vs Plugin(保留概念但给出现代化案例)
- Loader:负责将资源转换为 JavaScript 模块,例如使用
sass-loader
+dart-sass
,或自定义 Loader 将 Markdown + OpenAPI 转成页面组件。 - Plugin:在构建生命周期中扩展能力。现代案例:Vite 插件中利用
onResolve
拦截请求,将 AI 生成的 mock 数据注入开发服务器,提高接口未就绪时的迭代效率。
示例:在 Rspack/Vite 中集成 AI mock。
// vite.config.ts
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [
{
name: 'ai-mock',
configureServer(server) {
server.middlewares.use('/api/ai-demo', async (_, res) => {
const suggestion = await fetchLLMPrompt('生成电商推荐文案');
res.end(JSON.stringify({ suggestion }));
});
}
}
]
});
ts
3. 脚手架与项目初始化(Node 全栈思路)
- CRA/Vue CLI 几乎被 Vite/Nuxt/Next 取代,面试时强调
create-vite
、nitro
、create-t3-app
等现代脚手架。 - 全栈 Node 项目常结合
pnpm create turbo@latest
或nx init
建立共享代码库,并通过package.json#packageManager
约束团队版本。 - 关注“快速启动 + 权限治理”:示例说明
npm create cloudflare@latest
构建边缘函数,或npx sst create
直接包含 serverless 与 AI 推理模板。
4. Monorepo 与依赖治理(企业高频)
- 解释为何
pnpm
更适合多包仓库(符号链接 + 内容寻址),并展示pnpm workspaces
与changeset
配置。 - 结合 Node BFF,说明如何通过
Nx
的affected
图生成最小化测试矩阵,再借助 GitHub Actions/阿里云流水线实现渐进式部署。 - 面试亮点:提及“AI 代码助手仅作为 Review 参考,最终提交必须通过
pnpm lint
pnpm typecheck
与自动化用例”。
5. DevServer 与本地体验(提效与安全)
- Vite/Rspack 内置 HMR;可补充如何在 Docker/WSL 中开启
--host 0.0.0.0
以供多人协作。 - 面试加分:讲述如何使用
npm pkg set scripts.dev:ssr="NODE_OPTIONS=--enable-source-maps vite dev --ssr"
复现 SSR 问题。 - AI 相关:描述使用 LangChain.js 生成 API Mock、自动更新 Swagger,并通过
msw
/Mock Service Worker
注入浏览器,无需等待后端。
6. 构建优化与缓存体系(性能题)
- 使用增量缓存:
TurboRepo remote cache
、Nx Cloud
、Bazel
;在 CI 中通过pnpm fetch --prod
预安装依赖。 - Tree-shaking 与代码分割:面试常问如何优化“AI SDK 体积过大”。回答可以包含“只按需引入
openai/resources/chat
”、“使用rollup-plugin-visualizer
分析包体”。 - 图片/字体优化:引导使用
vite-imagetools
、Squoosh
,并提及在 Node 服务层引入 CDN、contenthash
、immutable headers
。
7. 工程规范与自动化(落地能力)
- Lint & Format:
eslint + @antfu/eslint-config
、biome
、prettier
。说明如何配置lint-staged
与 Husky,并在 Monorepo 中共享 config。 - Type Safety:
tsc --noEmit
、ts-reset
、zod
;面试经常延伸到“如何保证 API 与前端类型一致”,可以介绍openapi-typescript
或ts-rest
。 - Commit 与版本管理:遵守 Conventional Commits,结合
changesets
自动发版;给出自动生成 Release Notes 的 AI 应用(例如 GitHub Copilot 结合Release drafter
)。
8. CI/CD 与可观测性(全栈必问)
- 描述流水线:检测(lint/typecheck/test)→ 构建 → 镜像 → 部署(K8s/Serverless)。
- 重点强调“蓝绿 + 灰度 + 回滚”,以及如何在流水线里加入安全扫描(Snyk、Dependabot)和 AI 辅助 code review。
- 可观测:前端埋点 + Node BFF + LLM 推理日志统一打到 OpenTelemetry,再进 Grafana/Loki/阿里 SLS。
9. 微前端与跨端一体化(视野题)
- 模块联邦(Webpack/Rspack) vs Web Components vs iframe;补充国内常用解决方案(乾坤、EMP)。
- 面试延伸:当团队需要 Node BFF 聚合多个子应用时,如何统一身份认证、API 网关与 AI 推荐服务。
10. AI 驱动的工程提效(热点实践)
- 代码生成:Describe 你如何将 Copilot、Cursor、通义灵码嵌入到 Dev Workflow,并通过
prompt template
指导 AI 输出可编译代码。 - 文档同步:使用 OpenAI/Claude +
mdx-bundler
在 CI 中生成 FAQ、接口变更说明;强调人工校验。 - 质量保障:讲述如何用 LLM 做“智能单测生成 + 回归分析”,例如通过
pnpm exec vitest --run --config vitest.generated.config.ts
运行 AI 产出的额外用例。
11. 常见面试追问(准备答题套路)
- “如何在 2 周内落地一个 Node 全栈工程化体系?”
- 起步:
pnpm
+ Monorepo + Vite/Nest。 - 加固:
eslint
、vitest
、turbo
缓存。 - 自动化:GitHub Actions/飞书流水线,集成
sonar
与OWASP ZAP
。 - AI:引入 LLM 做代码审阅提示、生成 PR 摘要、辅助测试数据。
- 起步:
- “如何评估和迁移构建工具?”
- 评估指标:构建时长、兼容性、开发体验、Edge 支持。
- 验证步骤:搭 PoC → 覆盖关键页面 → 压测 → 计划分批切流。
参考资料:Vite 5 RFC、Rspack 官方实战、Nx 2024 状态报告、阿里《大规模前端工程化实践》、谷歌《LLM for DevEx》白皮书。
从应用场景上来看:
- webpack适用于大型复杂的前端站点构建。
- rollup适用于基础库的打包,如vue、react。
- parcel适用于简单的实验性项目,他可以满足低门槛的快速看到效果。
由于parcel在打包过程中给出的调试信息十分有限,所以一旦打包出错难以调试,所以不建议复杂的项目使用parcel
5. 有哪些常见的 Loader?他们是解决什么问题的?(中级)
- file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件。
- url-loader:和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去。
- source-map-loader:加载额外的 Source Map 文件,以方便断点调试。
- image-loader:加载并且压缩图片文件。
- babel-loader:把 ES6 转换成 ES5。
- css-loader:加载 CSS,支持模块化、压缩、文件导入等特性。
- style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS。
- eslint-loader:通过 ESLint 检查 JavaScript 代码。
6. 有哪些常见的 Plugin?他们是解决什么问题的?(中级)
- define-plugin:定义环境变量。
- commons-chunk-plugin:提取公共代码。
- uglifyjs-webpack-plugin:通过
UglifyES
压缩ES6
代码。
7. 如何利用 webpack 来优化前端性能?(提高性能和体验)(高级)
用 webpack 优化前端性能是指优化 webpack 的输出结果,让打包的最终结果在浏览器运行快速高效。
- 压缩代码。删除多余的代码、注释、简化代码的写法等等方式。可以利用 webpack 的
UglifyJsPlugin
和ParallelUglifyPlugin
来压缩 JS 文件, 利用cssnano
(css-loader?minimize)来压缩 css。 - 利用 CDN 加速。在构建过程中,将引用的静态资源路径修改为 CDN 上对应的路径。可以利用 webpack 对于
output
参数和各loader的publicPath
参数来修改资源路径。 - 删除死代码(Tree Shaking)。将代码中永远不会走到的片段删除掉。可以通过在启动 webpack 时追加参数
--optimize-minimize
来实现。 - 提取公共代码。
8.怎么配置单页应用?怎么配置多页应用?(高级)
单页应用可以理解为 webpack 的标准模式,直接在entry
中指定单页应用的入口即可,这里不再赘述。
多页应用的话,可以使用 webpack 的 AutoWebPlugin
来完成简单自动化的构建,但是前提是项目的目录结构必须遵守他预设的规范。 多页应用中要注意的是:
- 每个页面都有公共的代码,可以将这些代码抽离出来,避免重复的加载。比如,每个页面都引用了同一套 css 样式表。
- 随着业务的不断扩展,页面可能会不断的追加,所以一定要让入口的配置足够灵活,避免每次添加新页面还需要修改构建配置。
9.npm 打包时需要注意哪些?如何利用 webpack 来更好的构建?(高级)
Npm
是目前最大的 JavaScript 模块仓库,里面有来自全世界开发者上传的可复用模块。你可能只是 JS 模块的使用者,但是有些情况你也会去选择上传自己开发的模块。 关于 NPM 模块上传的方法可以去官网上进行学习,这里只讲解如何利用 webpack 来构建。
NPM 模块需要注意以下问题:
- 要支持 CommonJS 模块化规范,所以要求打包后的最后结果也遵守该规则。
- Npm 模块使用者的环境是不确定的,很有可能并不支持 ES6,所以打包的最后结果应该是采用ES5编写的。并且如果 ES5 是经过转换的,请最好连同 SourceMap 一同上传。
- Npm 包大小应该是尽量小(有些仓库会限制包大小)。
- 发布的模块不能将依赖的模块也一同打包,应该让用户选择性的去自行安装。这样可以避免模块应用者再次打包时出现底层模块被重复打包的情况。
- UI 组件类的模块应该将依赖的其它资源文件,例如
.css
文件也需要包含在发布的模块里。
基于以上需要注意的问题,我们可以对于 webpack 配置做以下扩展和优化:
- CommonJS 模块化规范的解决方案: 设置
output.libraryTarget='commonjs2'
使输出的代码符合CommonJS2 模块化规范,以供给其它模块导入使用。 - 输出ES5 代码的解决方案:使用
babel-loader
把 ES6 代码转换成 ES5 的代码。再通过开启devtool: 'source-map'
输出 SourceMap 以发布调试。 - Npm 包大小尽量小的解决方案:Babel 在把 ES6 代码转换成 ES5 代码时会注入一些辅助函数,最终导致每个输出的文件中都包含这段辅助函数的代码,造成了代码的冗余。解决方法是修改
.babelrc
文件,为其加入transform-runtime
插件。 - 不能将依赖模块打包到NPM模块中的解决方案:使用
externals
配置项来告诉 webpack 哪些模块不需要打包。 - 对于依赖的资源文件打包的解决方案:通过
css-loader
和extract-text-webpack-plugin
来实现,配置如下:
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
module: {
rules: [
{
// 增加对 CSS 文件的支持
test: /\.css/,
// 提取出 Chunk 中的 CSS 代码到单独的文件中
use: ExtractTextPlugin.extract({
use: ['css-loader']
}),
},
]
},
plugins: [
new ExtractTextPlugin({
// 输出的 CSS 文件名称
filename: 'index.css',
}),
],
};
javascript
↑