前端工程化:
开发环境
1)准备阶段:技术选型;规范(分支规范、UI规范)
2)开发阶段:开发、打包;测试(mocha、jest、Cypress 测试用例保证代码质量)
3)发布阶段:CI/CD持续集成和部署、docker、nginx
开发体验
前端:基础数据FCP、LCP;异常数据 接口异常、脚本异常;资源 白屏率、秒开率(performance API)
utils:业务API 业务组件 环境兼容
core:全局配置 数据处理 埋点上报
一、webpack
1、前端代码为什么要进行构建和打包?
1)模块化开发
2)编译高级语法(less、scss、ES6+、JSX、TS)
Babel转译:将 ES6+/TS/JSX 转换为 ES5 代码;Polyfill:Babel默认只转换新的JS句法,不转换新的API,如Iterator、Generator、Set、Map、Promise等全局对象,以及一些定义在全局对象上的方法如Object.assign;Css预处理
3)性能优化
代码压缩:压缩 JS、 CSS;Tree Shaking:删除未使用的代码(依赖 ESM 静态分析);代码分割(Code Splitting):拆分包,按需加载(如路由懒加载);缓存策略:生成 [contenthash] 文件名,利用浏览器缓存
4)开发体验
热更新(HMR):修改代码后局部更新,保留应用状态;Dev Server:提供本地开发环境(如 webpack-dev-server);Source Map:调试压缩后的代码时定位到源码
5)其他
资源管理与自动化:Loader 处理资源、HTML 中自动引入打包后的 JS/CSS;工程化与团队协作:Lint 工具、TS类型检查、CI/CD 集成;框架支持:React、vue等
统一高效的开发环境,统一的构建流程和产出标准,集成公司构建规范(提测、上线)
2、基本配置
Entry入口:指定打包的起点文件(如 src/index.js)
Output输出:定义打包后的文件存放位置(如 dist/main.js)
Loaders加载器:处理不同类型的文件
处理ES6 babel-loader
处理样式 style-loader css-loader less-loader postcss-loader(loader执行顺序从后往前)
处理图片 file-loader url-loader
Plugins插件:扩展功能(如 HTML 生成、代码压缩)
Mode模式:development(开发)或 production(生产)
环境管理:DefinePlugin 注入变量,区分 development 和 production
扩展能力:自定义Loader和Plugin
3、常见的loader和plugin?区别?
常见的loader如下:
- babel-loader/swc-loader:转译 ES6+/JSX/TypeScript → ES5
- css-loader:解析 CSS 中的 @import 和 url(),处理模块化 CSS
- style-loader:将 CSS 注入到 DOM(通过 style 标签),生产环境用MiniCssExtractPlugin.loader
- sass-loader/less-loader:编译 SCSS/SASS/LESS → CSS
- postcss-loader:处理 CSS 兼容性(如自动添加前缀)
- file-loader:将文件(如图片/字体)输出到 dist 目录,返回文件URL
- url-loader:小文件转为 Base64 内联,大文件交给 file-loader
- raw-loader:允许将文件处理成一个字符串导入
(file-loader、url-loader、raw-loader在webpack5被取代,type: asset/source功能相当于 raw-loader;asset/inline功能相当于url-loader;asset/resource功能相当于file-loader;asset默认会根据文件大小来选择使用哪种类型,当文件小于 8 KB 的时候会使用 asset/inline,否则会使用 asset/resource,也可手动进行阈值的设定)
常见的plugin如下:
- HtmlWebpackPlugin:打包结束后,自动生成一个html文件,并把打包生成的js模块引入该html中
- mini-css-extract-plugin:提取css到一个单独的文件中
- DefinePlugin:允许在编译时创建配置的全局对象,是一个webpack内置插件,不需要安装
- ReactRefreshWebpackPlugin:Webpack 配置中用于启用 React 组件热更新(HMR)的插件配置
- SplitChunksPlugin:用于优化代码分割的核心插件
- css-minimizer-webpack-plugin:css压缩
- terser-webpack-plugin:js压缩
- ImageMinimizerWebpackPlugin:图片压缩
区别
loader模块转换器,处理单个文件(转换、编译)(在 webpack内部,任何文件都是模块,不仅仅只是 js文件。默认情况下,在遇到import或者require文件打包加载模块的时候, webpack只支持对js和json文件打包。像 css、 sass、png等这些类型的文件的时候,需要配置对应的loader进行文件内容的解析);loader在模块加载阶段执行(文件级别)
plugin扩展插件,扩展Webpack 整体功能(打包优化、资源管理、环境变量注入等);plugins干预构建流程的各个阶段(整个流程)
4、module、chunk、bundle分别是什么?区别?
module(模块)各种源码文件,Webpack 将所有文件(包括 JS、CSS、图片等)视为模块,并通过依赖关系(如 import/require)构建依赖图;
chunk(代码块)构建过程中的中间产物,由一个或多个模块组成;
bundle(打包产物)chunk经过编译和打包后的最终输出文件(如 .js、.css),直接提供给浏览器运行的静态资源。
产生 Chunk 的场景
入口文件(Entry):每个入口文件生成一个 Chunk。
动态导入(Dynamic Import):import() 语法会自动生成新的 Chunk。
代码分割(SplitChunksPlugin):提取公共代码或第三方库到独立 Chunk。
5、webpack构建流程
初始化:合并配置、compiler、plugin registry。配置包括:优先级webapck.config.js.< npm script “start”: “webpack --XX” cli < env。从配置文件和 Shell语句中读取与合并参数,并初始化需要使用的插件和配置插件执行环境需要的参数。
编译构建 build:config、entry编译、loader针对源代码进行代码转换、AST、递归编译。从入口文件开始,针对每个Module调用对应的Loader,再找到该 Module依赖的Module,对每个依赖模块重复调用 loader 处理,递归地进行编译处理。loader 按配置顺序(从右到左、从下到上)处理模块。
生成 seal:chunk、assets。对编译后的Module组合成Chunk,把Chunk转换成文件最后输出
写入 emit
6、webpack性能优化
代码分割(Code Splitting):减少首屏加载时间,按需加载代码。包括:入口分割、动态导入、SplitChunksPlugin公共代码提取
缓存优化:[contenthash]
Tree shaking:删除未使用的代码
压缩代码:TerserPlugin等
懒加载(Lazy Loading):延迟加载非关键资源
预加载(Preload/Prefetch):Preload 立即加载关键资源;Prefetch 空闲时加载未来可能需要的资源
多进程并行处理
tree Shaking什么时候失效
tree shaking 用于消除代码中未使用的部分,依赖于ES Module的静态语法分析。
Tree Shaking 的触发条件:
1)静态模块:必须使用 ES6 模块语法(import/export)。CommonJS 的 require/module.exports 是动态的,无法被 Tree Shaking;import(‘./module.js’).then(…) 动态导入会导致整个模块无法被优化。
2)无副作用:Tree Shaking 只会移除没有副作用的代码。如果模块执行时会影响外部状态(如修改全局变量、DOM 操作),则不会被移除。
3)未被引用:未被其他模块引用的代码才会被移除。
4)工具支持:Webpack/Rollup 等工具配置正确。
Webpack:
必须启用 production 模式(自动开启 Tree Shaking)。
使用 optimization.usedExports: true 标记未使用的导出。
package.json中使用 sideEffects: false 告诉 Webpack 哪些模块没有副作用。
code splitting
目的
减少首屏加载时间:只加载当前页面需要的代码。
优化缓存:独立模块可单独缓存,避免整体重新加载。
提高运行时性能:减少主线程阻塞时间。
实现方式
1)入口分割。手动配置多个入口,适用于多页面应用(MPA)
module.exports = {
entry: {
home: './src/home.js',
about: './src/about.js',
},
output: {
filename: '[name].[contenthash].js',
},
};
2)动态导入,使用 import() 语法,ES6 动态导入(返回 Promise)
button.addEventListener('click', () => {
import('./module.js').then(({ func }) => {
func();
});
});
3)懒加载组件(React/Vue 等框架)
React:使用 React.lazy 和 Suspense。
Vue:使用异步组件定义
const LazyComponent = React.lazy(() => import('./LazyComponent'));
function App() {
return (
<div>
<React.Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</React.Suspense>
</div>
);
}
4)SplitChunks 插件(Webpack)
自动分割共享模块:提取公共依赖到单独的 chunk。
配置项:minSize、minChunks、cacheGroups 等。
module.exports = {
optimization: {
splitChunks: {
chunks: 'all', // 分割所有类型的 chunks
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
},
},
},
},
};
最佳实践
优先使用动态导入:灵活且自动化程度高。
避免过度分割:过小的 chunks 会增加 HTTP 请求数。
预加载(Preload)与预取(Prefetch)
<!-- 预加载:当前页面急需的资源 -->
<link rel="preload" href="critical.js" as="script">
<!-- 预取:未来可能需要的资源 -->
<link rel="prefetch" href="optional.js">
使用分析工具 webpack-bundle-analyzer分析包大小
7、webpack5相比webpack4新增了哪些功能?
1)资源模块(Asset Modules):在 Webpack5 之前,一般都会使用raw-loader、file-loader、url-loader来处理一些常见的静态资源,比如 PNG 图片、SVG 图标等等。Webpack5 提供了内置的静态资源构建能力,取代了 file-loader/url-loader/raw-loader
module.exports = {
...,
module: {
rules: [
{
test: /\.(png|jpg|svg|gif)$/,
type: 'asset/resource',
generator: {
// [ext]前面自带"."
filename: 'assets/[hash:8].[name][ext]',
},
},
],
},
}
2)内置 FileSystem Cache 能力加速二次构建:Webpack5 之前,我们会使用 cache-loader缓存一些性能开销较大的 loader ,或者是使用 hard-source-webpack-plugin为模块提供一些中间缓存。在 Webpack5 之后,默认就为我们集成了一种自带的缓存能力。
module.exports = {
...,
cache: {
type: 'filesystem',
...,
},
}
3)更强大的 Tree Shaking 能力:Tree Shaking 能力,是指能够在打包的过程中移除 JavaScript 上下文中未被引用到的变量,借以次来减少打包后的体积。webpack已经默认开启了这个功能,无需其他配置,但是使用这个会有条件。webpack5支持嵌套的 tree-shaking、支持 CommonJS 的 tree-shaking、支持内部模块的 sideEffects 标记
4)移除/废弃功能:移除了 Node.js polyfills(需要手动添加)、弃用了 require.include(已完全移除)、移除了 IgnorePlugin 的某些用法(需要显式指定上下文)
5)长期缓存优化(Long term cache): 防止模块顺序变化导致ID变化,避免无关修改影响所有chunk的hash
6)Module Federation(模块联邦):支持微前端架构,跨应用共享模块
Webpack 5 的这些改进使得构建更快、输出更小、配置更简单
参考:Webpack5 新特性
8、webpack的热更新原理
热模块替换(Hot Module Replacement, HMR)允许在不完全刷新页面的情况下更新修改的模块
Webpack HMR 原理解析
9、webpack proxy工作原理?为什么能解决跨域?
中间服务器webpack-dev-server,将自动编译和自动刷新浏览器等一系列对开发友好的功能全部集成在一起,目的是伪类提高日常开发效率,只用在开发阶段。
devServer里面proxy是关于代理的配置,proxy ⼯作原理实质上是利⽤ http-proxy-middleware 这个 http 代理中间件,实现请求转发给其他服务器。
工作流程如下:
1)浏览器发送请求(受同源策略限制):前端代码请求 http://localhost:8080/api/users(本地开发服务器)。
2)webpack-dev-server 拦截请求:检测到 /api 匹配代理规则,于是 不直接返回数据,而是 转发请求 到 http://backend-server.com/api/users。
3)目标服务器返回数据:后端服务器(如 http://backend-server.com)处理请求,返回数据给 webpack-dev-server。
4)webpack-dev-server 返回数据给浏览器:浏览器接收到数据,认为请求是来自 http://localhost:8080,因此 不会触发跨域错误。
浏览器的 同源策略(Same-Origin Policy) 限制的是:前端页面(如 http://localhost:8080) 不能直接通过 XMLHttpRequest 或 fetch 访问 不同源(如 http://backend-server.com) 的 API。
但 代理服务器 不受此限制,因为:服务器之间没有跨域限制(同源策略仅适用于浏览器)。webpack-dev-server 作为中间代理,代替浏览器发送请求,拿到数据后再返回给前端,浏览器看到的是同源响应,因此不会报跨域错误。
Webpack DevServer Proxy:本地环境
nginx反向代理:生产环境
二、vite
为什么打包速度快
原生 ES 模块 (ESM) 支持
按需编译:开发服务器只编译当前页面需要的模块
预构建(Pre-Bundling)优化:使用 esbuild预构建依赖项,将 CommonJS/UMD 依赖转换为 ESM 格式;合并多个小文件减少网络请求(结果缓存到 node_modules/.vite,仅在依赖变化时重新构建)
高效的热更新
生产构建优化:开发用 esbuild 实现极速体验;生产用 Rollup 保证最佳打包质量
vite config
resolve
devServer
build
optimizeDeps
Vite 的 HMR(热更新)是如何实现的?
Vite 的 HMR 基于原生 ESM 实现,核心流程:
服务器与浏览器通过 WebSocket 建立连接。
当文件修改时,服务器发送更新通知到浏览器。
浏览器接收更新后,仅替换变更的模块,保持应用状态。
Vite 如何处理 CSS、TypeScript 和静态资源
- CSS
直接支持 import ‘./style.css’,自动处理路径和 HMR。
内置 CSS Modules(.module.css)和 PostCSS 支持。 - TypeScript
无需 Babel,直接通过 esbuild 编译(仅类型检查,不转换语法)。
需配合 tsconfig.json,但运行时依赖浏览器原生支持。 - 静态资源
自动处理资源导入(如 import img from ‘./logo.png’)。
小于 4KB 的资源会内联为 Base64(可通过 build.assetsInlineLimit 配置)。
Vite Plugin
@vitejs/plugin-react:React 支持
@vitejs/plugin-vue:Vue 支持
vite-plugin-html:html优化
vite-plugin-image-optimizer:图片压缩
vite-plugin-compression:资源压缩
vite-plugin-remove-console:移除生产环境的console
rollup-plugin-visualizer:可视化分析打包结果
unplugin-auto-import:自动导入常用API
三、babel
其他
pnpm、npm、yarn
npm 生态最成熟,锁文件package-lock.json
yarn 在大型项目中表现稳定,支持离线模式,锁文件yarn.lock
npm和yarn都采用扁平化依赖树(hoisting),会将依赖提升到顶层 node_modules,可能导致版本冲突。
pnpm 使用硬链接和符号链接的方式管理依赖,锁文件pnpm-lock.yaml。适合追求性能和磁盘空间优化、需要严格依赖隔离的项目,或使用 monorepo 架构的项目
性能对比
- 安装速度:pnpm > yarn > npm(通常情况下)。pnpm 因共享依赖存储,重复安装时无需重新下载;yarn 早期通过并行安装提升速度,npm 后期版本也在性能上有较大优化。
- 磁盘空间:pnpm 最优,因为依赖被全局存储并复用;npm 和 yarn 可能在不同项目中重复存储相同依赖。
monorepo、multirepo
monorepo 单一代码仓库
multirepo 多代码仓库
monorepo包管理工具lerna、yarn workspace、pnpm workspace、nx、trubo
微前端、MF模块联邦