一、模块化规范
1.1 模块化概述
前端模块化是将大型前端项目拆分为独立、可复用模块的标准化方法。它解决了以下问题:
- 变量污染:避免全局变量冲突
- 依赖管理:清晰管理模块间的依赖关系
- 代码复用:提高代码的复用性和可维护性
- 按需加载:支持动态加载,提升性能
模块化规范主要分为四种:CommonJS、AMD、CMD、ES Module。
1.2 CommonJS
定义:服务器端模块规范,由 Node.js 推广使用,Webpack 也采用这种规范。
核心特点:
| 特性 | 说明 |
|---|---|
| 模块定义 | 每个文件就是一个模块,拥有独立作用域 |
| 模块标识 | require() 方法的参数,支持小驼峰命名、相对路径、绝对路径 |
| 模块引用 | require() 同步加载模块 |
| 模块导出 | 使用 module.exports 或 exports 向外暴露接口 |
示例:
// 导出模块
// utils.js
const sum = (a, b) => a + b;
module.exports = { sum };
// 导入模块
// app.js
const { sum } = require("./utils");
console.log(sum(1, 2)); // 3
优缺点:
| 优点 | 缺点 |
|---|---|
| 解决变量污染问题 | 同步加载,不适合浏览器环境 |
| 模块定义简单、接口简洁 | 没有并行加载机制 |
| 支持引入和导出,便于依赖管理 | 浏览器端需要从服务器加载,可能导致假死 |
1.3 AMD(Asynchronous Module Definition)
定义:浏览器端异步模块定义规范,由 RequireJS 推广。
核心特点:
- 依赖前置:定义模块时声明所有依赖
- 异步加载:不会阻塞浏览器渲染
示例:
// 定义模块
define(["jquery"], function ($) {
return {
init: function () {
$("body").css("background", "red");
},
};
});
// 加载模块
require(["./module"], function (module) {
module.init();
});
优缺点:
| 优点 | 缺点 |
|---|---|
| 用户体验好,无延迟 | 依赖前置可能加载未使用的模块 |
| 异步加载不阻塞页面 | 代码书写复杂 |
1.4 CMD(Common Module Definition)
定义:通用模块定义规范,由 SeaJS 推广。
核心特点:
- 依赖就近:用到时才
require - 按需加载:性能更好
示例:
// 定义模块
define(function (require, exports, module) {
// 依赖就近声明
const $ = require("jquery");
exports.init = function () {
$("body").css("background", "red");
};
});
优缺点:
| 优点 | 缺点 |
|---|---|
| 性能好,按需加载 | 依赖分析复杂 |
| 代码书写自然 | 生态不如 AMD |
1.5 ES Module
定义:ES6 官方模块化规范,现已成为前端主流。
核心特点:
| 特性 | 说明 |
|---|---|
| 静态导入 | import 语句在编译时执行 |
| 动态导入 | import() 返回 Promise,支持按需加载 |
| 模块导出 | export 导出,export default 默认导出 |
| 严格模式 | 模块自动运行在严格模式下 |
示例:
// 导出模块
// utils.js
export const sum = (a, b) => a + b;
export default function () {
console.log("default export");
}
// 导入模块
// app.js
import defaultFn, { sum } from "./utils";
defaultFn(); // 'default export'
console.log(sum(1, 2)); // 3
// 动态导入
import("./utils").then(({ sum }) => {
console.log(sum(1, 2));
});
优缺点:
| 优点 | 缺点 |
|---|---|
| 官方标准,语法简洁 | 旧浏览器不支持,需要编译 |
| 静态分析,支持 Tree Shaking | 动态导入需要 Promise 支持 |
| 支持循环引用 | 模块路径必须完整 |
1.6 四种规范对比
| 特性 | CommonJS | AMD | CMD | ES Module |
|---|---|---|---|---|
| 加载方式 | 同步 | 异步 | 异步 | 静态(编译时) |
| 依赖声明 | 动态 | 前置 | 就近 | 静态 |
| 适用环境 | 服务器端 | 浏览器端 | 浏览器端 | 通用 |
| 支持循环引用 | 是 | 是 | 是 | 是 |
| Tree Shaking | 不支持 | 不支持 | 不支持 | 支持 |
| 模块路径完整性 | 必须完整 | 必须完整 | 必须完整 | 必须完整 |
二、Webpack 构建工具
2.1 Webpack 打包原理
核心概念:
- 依赖分析:递归构建依赖关系图
- 模块转换:通过 Loader 转换不同类型文件
- 代码优化:压缩、去重、Tree Shaking
- 资源输出:生成静态资源 bundle
打包流程:
入口文件 → 依赖分析 → 模块转换(Loader) → 代码优化(Plugin) → 输出bundlewebpack 打包原理是根据文件间的依赖关系对其进行静态分析,将这些模块按指定规则生成静态资源。当 webpack 处理程序时,它会递归地构建一个依赖关系图,其中包含应用程序需要的每个模块,将所有这些模块打包成一个或多个 bundle。
webpack 有两种组织模块的依赖方式:同步、异步。异步依赖将作为分割点,形成一个新的块;在优化了依赖树之后,每一个异步区块都将作为一个文件被打包。
webpack 有一个智能解析器,几乎可以处理任何第三方库。无论它们的模块形式是 CommonJS、AMD 还是普通的 JS 文件;甚至在加载依赖的时候,允许使用动态表 require("/templates/"+name+".jade")。
2.2 Webpack 配置结构
模块组成:import、export、require
module.exports = {
mode: "development", // 模式:development | production
entry: "./src/index.js", // 入口
output: {
// 输出
filename: "bundle.js",
path: "./dist",
},
module: {
rules: [
// Loader 配置
{ test: /\.js$/, use: "babel-loader" },
{ test: /\.css$/, use: ["style-loader", "css-loader"] },
],
},
plugins: [
// Plugin 配置
new HtmlWebpackPlugin(),
],
};
webpack4.0优化点详解:
零配置模式:
- Webpack 4.0 带来了零配置体验,无需手动编写复杂的 webpack.config.js
- 默认入口:
./src/index.js - 默认输出:
./dist/main.js - 默认模式:根据环境自动选择 development 或 production
SplitChunksPlugin:
- 内置代码分割插件,无需额外安装
- 替代了 Webpack 3.x 中的 CommonsChunkPlugin
- 自动提取多个 chunk 中的公共代码
- 支持按需加载优化
Mode 模式:
development模式:开发环境优化,保留调试信息production模式:生产环境优化,自动启用代码压缩、Tree Shaking 等- 只需设置
mode: 'production'或mode: 'development'
Tree Shaking:
- 生产模式下自动开启
- 通过静态分析移除未使用的代码
- 要求使用 ES Module 语法(import/export)
- 需要设置
sideEffects: false配合
缓存优化:
cache-loader:缓存 loader 的编译结果,加速二次构建hard-source-webpack-plugin:缓存模块解析结果
多线程打包:
thread-loader:将耗时的 loader 分配到 worker 池中执行happyPack:多进程并行处理文件- 显著加速大型项目的构建速度
2.3 常用 Loader
Loader 定义:是一个导出为函数的 javascript 模块,根据 rule 匹配文件扩展名,处理文件的转换器。
| Loader | 作用 | 使用场景 |
|---|---|---|
| babel-loader | ES6+ 转 ES5 |
处理 JavaScript |
| css-loader | 加载 CSS,支持模块化、压缩、文件导入等特性 |
处理 CSS |
| style-loader | 把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS |
开发环境快速预览 |
| sass-loader | SCSS/SASS 转 CSS |
处理预处理器 |
| file-loader | 把文件输出到指定目录,在代码中通过相对 URL 去引用输出的文件 |
处理图片、字体 |
| url-loader | 设置一个阈值,小于阈值时把文件 base64 编码,大于阈值时交给 file-loader 处理 |
优化小资源加载 |
| postcss-loader | 扩展 CSS 语法,使用下一代 CSS,可以配合 autoprefixer 插件自动补齐 CSS3 前缀 |
自动补全前缀 |
| image-loader | 加载并且压缩图片文件 | 优化图片 |
| eslint-loader | 通过 ESLint 检查 JavaScript 代码 |
代码检查 |
2.4 常用 Plugin
Plugin 定义:本质是插件,基于事件流框架 Tapable,插件可以扩展 Webpack 的功能,在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
| Plugin | 作用 | 使用场景 |
|---|---|---|
| html-webpack-plugin | 简化 HTML 文件创建 (依赖于 html-loader) |
自动引入 bundle |
| mini-css-extract-plugin | 分离样式文件,CSS 提取为独立文件,支持按需加载 (替代 extract-text-webpack-plugin) |
生产环境分离样式 |
| terser-webpack-plugin | 压缩 JavaScript |
生产环境优化 |
| clean-webpack-plugin | 清空输出目录 | 构建前清理 |
| split-chunks-plugin | 代码分割 | 提取公共代码(Webpack 4 内置) |
| uglifyjs-webpack-plugin | 压缩 JavaScript 文件 |
代码压缩 |
| optimize-css-assets-webpack-plugin | 压缩 CSS 文件 |
CSS 优化 |
2.5 Loader vs Plugin 对比
| 对比维度 | Loader | Plugin |
|---|---|---|
| 配置位置 | module.rules |
plugins |
| 作用对象 | 单个文件 | 整个构建过程 |
| 执行时机 | 模块解析阶段 | 构建的各个阶段 |
| 实现方式 | 导出函数 | 类实例 |
Loader在module.rules中配置,作为模块的解析规则,类型为数组。每一项都是一个Object,内部包含了test(类型文件)、loader、options(参数)等属性。Plugin在plugins中单独配置,类型为数组,每一项是一个Plugin的实例,参数都通过构造函数传入。
2.6 代码分离策略
代码分离:将代码分割成多个 bundle,按需加载,提升首屏性能。
| 分离方式 | 说明 | 配置方式 |
|---|---|---|
| 入口起点 | 手动配置多个入口 | entry: { main: './src/main.js' } |
| 防止重复 | 使用 SplitChunksPlugin 去重和分离 chunk | splitChunks: { chunks: 'all' } |
| 动态导入 | 运行时按需加载 | import('./module') |
| 第三方库分离 | 单独打包第三方依赖 | splitChunks.cacheGroups.vendor |
webpack如何实现代码分离:
- 入口起点:使用
entry配置手动地分离代码 - 防止重复:使用
SplitChunksPlugin去重和分离chunk(Webpack 4 内置) - 动态导入:通过模块的内联函数调用来分离代码
示例:动态导入
// 路由懒加载
const Home = () => import("./Home.vue");
const About = () => import("./About.vue");
const routes = [
{ path: "/", component: Home },
{ path: "/about", component: About },
];
2.7 Webpack 性能优化策略
构建优化:
缓存策略:使用
cache-loader、hard-source-webpack-plugin并行构建:
thread-loader、happyPack减少解析:
include/exclude限制解析范围CDN 引入:将第三方库通过 CDN 引入,不打包
代码分割:
- 入口起点分割:多个入口文件
- 公共代码分割:提取重复代码
- 动态导入:按需加载模块
- 第三方库分割:单独打包第三方依赖
Tree Shaking:
- 启用 production 模式
- 使用 ES6 模块语法
- 避免副作用
- 配置
sideEffects
懒加载:
- 路由懒加载:
import()动态导入 - 组件懒加载:需要时再加载
- 图片懒加载:滚动到可视区域再加载
- 路由懒加载:
缓存配置:
- 文件名哈希:
[contenthash] - 长期缓存:分离runtime chunk
- 缓存loader结果:
cacheDirectory - 模块标识符:
ModuleScopePlugin
- 文件名哈希:
Babel优化:
- 避免不必要的转译:使用
include或exclude配置,不转译node_modules中的 js 文件 - 缓存转译结果:设置
loader: 'babel-loader?cacheDirectory=true',减少重复转译 - 使用
@babel/preset-env:按需转译 - 启用
core-js:按需导入 polyfill
插件优化:
- 使用
HtmlWebpackPlugin:自动生成HTML - 使用
MiniCssExtractPlugin:提取CSS到独立文件,减少HTML大小 - 使用
OptimizeCssAssetsPlugin:压缩CSS文件 - 使用
TerserWebpackPlugin:压缩JS文件
输出优化:
- 代码压缩:TerserPlugin 压缩 JS,CSSMinimizerPlugin 压缩 CSS
- Tree Shaking:确保使用 ES Module,开启
sideEffects: false - 代码分割:提取公共代码、按需加载
- 资源优化:图片压缩、Base64 内联小资源
图片优化:
- 压缩图片:
- 使用工具:TinyPNG、ImageOptim
- Webpack插件:
image-webpack-loader - 保持合理的质量:70-80%
- 使用适当的图片格式:
- JPEG:适合照片等复杂图像
- PNG:适合图标、透明图像
- WebP:现代浏览器支持,更小体积
- SVG:适合图标、矢量图形
- AVIF:新一代格式,更高压缩率
- 响应式图片:
srcset属性:提供不同尺寸的图片sizes属性:指定图片显示尺寸<picture>元素:根据设备选择图片- 服务端自适应:根据设备提供合适尺寸
- 图片懒加载:
loading="lazy"属性- 自定义懒加载库
- Intersection Observer API
- 滚动事件监听(传统方式)
- 其他图片优化:
- 使用CDN托管图片
- 图片CDN:自动处理图片尺寸
- 避免图片缩放:使用合适尺寸
- 图标字体:替代小图标
2.8 其他构建优化
Gzip/Brotli压缩:
- 前端配置:在request headers中添加
accept-encoding: gzip - webpack配置:使用
CompressionWebpackPlugin插件 - 服务端配置:Nginx启用gzip
- 压缩格式:支持gzip、deflate、br(Brotli)
构建速度优化:
- 多线程构建:
thread-loader - 并行压缩:
parallel: true - 减少搜索范围:
resolve.modules - 缓存:
webpack.cache
体积优化:
- 移除未使用的代码
- 按需加载第三方库
- 使用更小的替代库
- 代码分割和按需加载
三、工程化最佳实践
3.1 持续集成与持续部署(CI/CD)
CI(持续集成):
- 自动构建、测试
- 尽早发现问题
- 工具:GitHub Actions、GitLab CI、Jenkins
CD(持续部署):
- 自动部署到生产环境
- 蓝绿部署、滚动更新
- 工具:Docker、Kubernetes
3.2 代码质量保障
代码规范:
- ESLint:JavaScript 代码检查
- Prettier:代码格式化
- Stylelint:CSS 代码检查
- Git钩子工具:Husky、lint-staged(自动化规范检查)
自动化测试:
- 单元测试:Jest、Mocha
- 集成测试:Cypress、Playwright
- E2E 测试:Cypress、TestCafe
3.3 性能监控
监控指标:
- Core Web Vitals:LCP、FID、CLS
- 错误监控:Sentry
- 性能监控:Lighthouse、Web Vitals Extension
优化方向:
- 首屏加载时间
- 页面响应时间
- 内存使用情况