浅谈前端工程化理解
前言
本文是个人学习实践过程中的记录及理解,如有错漏欢迎指出。
相关知识来源于
哲玄前端(抖音ID:44622831736)大前端全栈实践课程
什么叫前端工程化?
一句话概括:把前端开发流程系统化、自动化、规范化,让代码像"产品"一样可以被构建、测试、部署和维护。 其中包括 规范、工具链、流程、体系。
而这样的"工业流水线"为了解决这些痛点:
- 代码无规范 → 难维护
- 手工构建、发布 → 易出错、效率低
- 页面越来越复杂 → 打包体积、性能成瓶颈
- 团队协作缺乏统一 → 冲突频繁,复用差
- 上线风险高 → 缺少测试、监控、回滚机制
工程化涉及的核心环节
-
开发阶段
-
模块化 / 组件化:
- JS 模块化(
ESM
、CommonJS
) - CSS 模块化(
CSS Modules
、预处理器) - 组件化(
React/Vue/Svelte
等)
- JS 模块化(
-
语法增强:
Babel/SWC
转译,TypeScript
静态类型PostCSS
、Less
、Sass
等
-
代码规范:
ESLint
、Prettier
、Stylelint
Git hooks
Commit message
规范(commitlint
,conventional commits
)
-
-
构建阶段
- 打包构建工具 :
Webpack
、Vite
、Rollup
- 资源优化 :
Tree Shaking
、Code Splitting
、懒加载 - 多环境配置 :
dev/test/staging/prod
- 打包构建工具 :
-
测试阶段
- 单元测试 :
Jest
、Vitest
、Mocha
- 集成测试 :
Cypress
、Playwright
- 端到端测试(E2E) :保证整体业务流程正确
- 单元测试 :
-
发布 & 运维阶段
- 持续集成 / 持续部署(CI/CD) :
GitHub Actions
、GitLab CI
、Jenkins
- 自动化构建:push → 构建 → 测试 → 部署 → 通知
- 灰度发布 / A/B 测试 / 版本回滚
- CDN 缓存、前端监控(埋点、性能、错误上报)
- 持续集成 / 持续部署(CI/CD) :
-
协作 & 维护
Monorepo
/ 多包管理 (Turborepo
、Lerna
)- 文档体系 (
Storybook
、docusaurus
、typedoc
) - 脚手架 / 模板化 (
Vue CLI
等) - 持续优化:性能指标、包体积分析、依赖治理
随着前端技术愈发成熟和工作团队的扩张,开发过程中我们会愈发注重模块化、组件化、类型约束、代码编程规范等,诞生出包括像 Egg、Salis 等奉行"约定优于配置" 的框架,以及课程中的 eplis-core ,这些工具能够帮助团队按照 一套统一的约定 开发应用,帮助开发团队和开发人员降低开发和维护成本。
这些涉及开发阶段的工具和操作应该是工作中经常遇到,是我们所熟悉的。这时应该意识到,不是仅仅"用 webpack
等打包工具就是工程化" ,其实我们一直参与到某个 理念 + 实践体系 中,其目标是 用工程化方法论去管理和优化前端开发的整个生命周期。
我们接触最多的除开发阶段外,便是构建阶段,该产出与我们系统性能息息相关。
工程化构建
开发一个系统通常会涉及到大量的代码,为了提升开发效率,也为了方便团队管理维护,开发者会像上述按照一定的约定或规范进行开发。
而浏览器并不能完全直接识别这些文件,所以我们需要将其转换成浏览器(或者某个应用)能够识别、执行的文件,然后在浏览器中完成页面展示。
这时,借助打包工具,我们可以大大缩短人力成本,仅通过一条命令,便可让工具按照预设的配置一步到位输出预想产物。这将极大地提高打包效率。
那么这些工具做了什么呢?
它会按照我们提供的入口文件,读取与之相关的模块及依赖,按照我们提供的规则,规定何种类型文件使用何种 解析器 (某些解析器需借助额外的插件,如 vue-loader
)解析编译,转译完成后打包输出,我们可以将其注入到某个页面中,便可获得浏览器能够识别、执行的页面。
不过这仅仅是冰山一角,现打包工具拥有成熟的插件机制,具备很高的可扩展性,可根据自己的业务场景定制配置,从而极大降低了应用的开发成本。
本次学习中使用的工具是
webpack
,所以用webpack
举例。但请注意,什么工具不重要,只要能实现你想要的效果。
比如:
-
混用 SPA + MPA
通常 单页面应用 (SPA) 通过插件按照指定模板生成一个 HTML 文件,将 chunk 全部注入该页面,打包出一个入口(通常是
index.html
)。而页面内容的变化,通过 前端路由 + JS 动态渲染 来完成,用户点击链接时,不会重新请求整个页面,而是局部更新。✅ 优点:
- 前后端分离,前端控制路由与渲染,交互流畅。
- 体验接近原生 App,切换页面不卡顿。
- 资源可缓存,后续访问快。
❌ 缺点:
- 首屏加载慢(要加载完整的 JS 框架、路由、业务逻辑)。-
- SEO 不友好(因为页面内容由 JS 渲染)。
- 复杂应用里,打包体积大,可能影响性能。
而多页面应用 (MPA) 则是每个页面对应一个 独立的 HTML 文件 ,请求新页面时,浏览器会重新加载 HTML、CSS、JS,页面之间 没有前端路由的强绑定,切换页面会整页刷新。
✅ 优点:
- 首屏快(直接返回服务端渲染好的 HTML)。
- SEO 友好(内容直接在 HTML 中,搜索引擎容易抓取)。
- 适合业务隔离(不同页面可以独立构建、独立部署)。
❌ 缺点:
- 页面切换慢,每次都要重新加载资源。
- 前后端耦合度高,不便于前后端分离。
- 公共资源(JS/CSS)可能重复加载。
为了 平衡体验、性能、维护成本,我们可以适当结合两者,通过多入口打包,但每个入口内部又是一个 SPA。
以
webpack
为例,我们提供多个打包入口配置,借助HtmlWebpackPlugin
插件根据入口配置生成相应的 HTML 文件,其会将每个打包入口配置中文件所引用的资源分别注入到各自的 HTML 文件中,从而生成多个 系统入口。这时每个 SPA 内部用 前端路由(Vue Router、React Router) 管理页面切换,而系统入口通过 服务端路由 控制访问。
-
热更新
模块热替换(hot module replacement 简称 HMR) 是
webpack
提供的功能之一。webpack
提供了 HMR 功能,需做的仅需更新webpack-dev-server
的配置,便使用webpack
内置的 HMR 插件。如果你在技术选型中使用了
webpack-dev-middleware
而没有使用webpack-dev-server
,请使用webpack-hot-middleware
依赖包,以在你的自定义服务器或应用程序上启用 HMR。javamodule.exports = { devServer: { static: './dist', hot: true, } }
等多关于
webpack-dev-server
的配置详情可以查看这里。其工作原理:
- 监控到源文件发生改变
- 重新构建打包到指定位置(一般为内存)
- 通知应用程序资源发生改变,检查更新
- 应用程序异步下载更新,并同步应用更新
如果我们使用自定义服务器启用 HMR 会发现:
webpack-dev-middleware
负责监控源文件的改变,并按传入编译器重新构建。webpack-hot-middleware
负责监测更新信息,并通知应用程序更新。HotModuleReplacementPlugin
接收更信信息后,完成下载并应用更新。
优化
-
分包
项目在开发过程中,我们会复用部分代码以提升开发效率,但在打包时这部分代码会根据入口文件引用重复打包到多个入口的
chunk
中,这很明显不是我们希望的。这时便需要为打包工具添加分包策略,虽然像
webpack
具有默认分包策略,但默认情况下,它只会影响到按需加载的 chunks。我们想要获得的分包情况:
-
体积较大、不常更改的独立第三方库及生态依赖,如
Vue
、Pinia
➡︎Vue
包 -
剩余的第三方库 ➡︎ vendor 包
-
公共业务代码 ➡︎ common 包
-
不符合上诉情况,根据入口配置正常打包
如果
UI
库使用的是按需加载,建议单独分为一个包,避免因为加载的组件前后不一,第三方库的包contenthash
等改变,导致缓存失效。这样分包的好处是可以使用浏览器可以并行下载的优势,并且不常改变的包可以被浏览器有效缓存起来。
分包不是越多越好。首先服务器不支持http2.0的情况不支持多路复用,浏览器(谷歌)只能同时并发同域名的6个请求,零碎的 chunk 可能会增加 请求数(即使 HTTP/2/3 也有开销),所以请权衡利弊后再按需求制定分包策略。
-
-
优化
loader
转译过程-
利用多线程加速耗时操作
使用
happypack
、thread-loader
帮助开启线程池,将工作交给多个工作线程处理。✅
thread-laoder
是webpack
官方支持的,同时也支持缓存和 worker 复用,能缩短二次构建时间。❌
happypack
已经停止维护,适合webpack
旧版本使用。有 开启进程开销,小文件/轻量 loader 反而可能更慢。
推荐仅在耗时的操作中使用,适合 CPU 密集型 loader。
使用时也切勿开启过多工作线程将 CPU 占满,导致 I/O 操作切换频繁。
- 细分
loader
需要处理的范围,充分利用include
、exclude
等属性
-
-
提取公共
css
样式并压缩体积-
使用 mini-css-extract-plugin 根据 CSS 文件中的样式使用情况,将公共部分提取到一起
此插件不能与 loader 链中的
style-loader
一同使用!!!可按需求配合
splitChunks.cacheGroups
分包,可将提取到的 CSS 文件放到一个独立文件中。yamlsplitChunks: { cacheGroups: { styles: { type: "css/mini-extract", // type 用于匹配 .css 文件 name: "styles", chunks: "all", enforce: true, // 强制执行 }, }, },
-
css-minimizer-webpack-plugin 将提取出来的 CSS 资源进行压缩
-
-
压缩
js
文件体积Terser
与esbuild
皆是优秀的解析器/压缩器工具集。EsbuildPlugin
拥有极致的性能,但压缩产物体积稍大一些。TerserWebpackPlugin
拥有海量配置,兼容性强,可以进行非常激进的代码压缩和转换,往往能得到最小的产物体积。
这是一场"时间换空间"的经典权衡。
EsbuildPlugin
用微不足道的空间(体积略增)换取了巨大的时间节省(构建速度飞跃),而TerserWebpackPlugin
则愿意花费更多时间来打磨出体积最小的代码。 对于大多数现代项目而言,esbuild
带来的开发体验提升往往比那一点点的体积优化更有价值。
-
source-map
映射源根据不同环境选择不同策略选择一种 source map 风格来增强调试过程。不同的值会明显影响到构建(build)和重新构建(rebuild)的速度。
arduino// webpack 通过配置项 devtool 切换 devtool: "[inline-|hidden-|eval-][cheap-[module-]]source-map"
eval-
→ 用eval()
包裹模块,构建快。inline-
→ source map 直接内联到 JS 中。hidden-
→ 生成 map 文件,但不在 JS 里声明引用。cheap-
→ 只映射到行,不包含列,不包含 loader 前的源码。module-
→ 包含 loader 转换前的源码映射(更精确)。
- 合理使用浏览器缓存,给产物文件添加
contenthash
、hash
、chunkHash
的设置
- 打开构建缓存落盘,加快二次构建
- 充分运用
tree-shaking
(树摇)
以上便是此次学习的全部总结,当然这些只是构建知识的部分应用,更是前端工程化工作中的冰山一角,未来还有许多知识、理念需要了解与掌握,希望能抛砖引玉,如果有更多有意思的知识,欢迎分享。