注:全文理解源于:抖音“哲玄前端”《大前端全栈实践》
webpack是什么
webpack 是JavaScript 应用程序的静态模块打包工具。在前端开发中,我们的项目通常由众多的 JavaScript 模块、CSS 文件、图片等资源组成。webpack 能够智能地分析这些资源之间的依赖关系,并将它们打包成适合在浏览器中加载的静态文件。
webpack 的核心特性
1. 模块打包
Webpack 支持多种模块规范,包括 CommonJS、AMD 和 ES6 模块。无论你使用哪种模块化方式,Webpack 都能将其打包成一个或多个输出文件。通过配置 entry 属性,你可以指定项目的入口文件,Webpack 会从这里开始解析依赖。
2. 加载器(Loaders)
在前端开发中,我们不仅处理 JavaScript 文件,还有 CSS、图片、字体等各类资源。Webpack 的加载器(Loaders)允许你将这些非 JavaScript 文件转换为 JavaScript 模块,从而可以被 Webpack 打包。
3. 插件系统(Plugins)
Webpack 的插件系统是其强大的扩展机制。插件可以在打包过程的各个阶段执行任务,比如优化代码、管理资源、注入环境变量等。
4. 代码分割与懒加载
为了优化应用的加载性能,Webpack 提供了代码分割和懒加载的功能。代码分割可以将代码拆分成多个块(Chunks),然后按需加载。
5. 热模块替换(HMR)
热模块替换(Hot Module Replacement,HMR)是 Webpack 的一大亮点功能。它允许在应用运行时,替换修改过的模块,而无需刷新整个页面。这对于前端开发来说,极大地提高了调试效率。
webpack 在前端工程化中的实践
基础配置文件 webpack.config.js
- entry:指定了项目的入口文件。
- output:定义了打包后文件的输出路径和文件名。
- module:可以为不同类型的文件设置对应的加载器。比如,处理 CSS 文件时使用
style-loader和css-loader,处理 JavaScript 文件时使用babel-loader进行语法转换。- plugins:这里可以配置各种插件实例,以增强 webpack 的功能。比如,
HtmlWebpackPlugin会根据模板生成 HTML 文件,MiniCssExtractPlugin会提取 CSS 文件等。- optimization:这里配置代码分割,模块合并,缓存,TreeShaking,压缩等优化策略
用到的loader
- vue-loader:用于处理
.vue文件,这是 Vue.js 单文件组件的文件格式。它将模板(HTML)、脚本(JavaScript)和样式(CSS)三部分组合在一起的文件解析并打包。 - babel-loader:用于将 ES6/ES7+ 转换为兼容浏览器的 JavaScript。
- style-loader:将 CSS 代码注入到 HTML 页面中,通过创建
<style>标签或将 CSS 代码作为字符串插入到页面中。 - css-loader:解析 CSS 文件中的
@import和url()等依赖关系,并将它们转换为模块化的形式,使 Webpack 能够处理这些依赖。 - sass-loader:将 Sass 或 SCSS 文件编译为普通的 CSS 文件。
- file-loader:处理文件资源,将文件输出到指定目录,并返回文件的相对路径。常用于处理图片、字体等静态资源。当你需要在代码中引入图片、字体等文件时,
file-loader会将这些文件复制到输出目录中,并返回可以在代码中使用的相对路径。 - thread-loader:用于将 Loader 的工作分配到多个线程中运行,提高构建性能。它可以在支持多线程的环境中并行处理 Loader 的任务。
// 模块解析配置(决定了要加载解析哪些模块,以及用什么方式去解析)
module: {
rules: [
{
test: /\.vue$/,
use: {
loader: 'vue-loader'
}
},
{
test: /\.js$/,
// 只对业务代码进行 babel,加快 webpack 打包速度
include: [
path.resolve(process.cwd(), './app/pages')
],
use: {
loader: 'babel-loader'
}
},
{
test: /\.(png|jpe?g|gif)(\?.+)?$/,
use: {
loader: 'url-loader',
options: {
limit: 300,
esModule: false
}
}
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
},
{
test: /\.(eot|svg|ttf|woff|woff2)(\?\S*)?$/,
use: 'file-loader'
}
]
},
plugins配置
-
VueLoaderPlugin
它的职能是将你定义过的其他规则复制并应用到 .vue 文件里;例如,如果有一条匹配规则 /.js$/ 的规则,那么它会应用到 .vue 文件中的
-
HtmlWebpackPlugin
它会根据指定的模板文件或默认模板生成一个 HTML 文件,并自动将 Webpack 打包后的 JavaScript 文件和 CSS 文件(如果使用了 CSS 提取插件)引入到 HTML 文件中。
-
CleanWebpackPlugin
在每次构建之前,自动清除输出目录中的旧文件,确保每次构建都是最新的,避免因旧文件残留而引发的问题。
-
webpack.ProvidePlugin
允许你为模块的依赖关系提供一个全局的变量,这样在代码中使用这些变量时就不需要每次都进行导入,简化了代码的编写。
-
webpack.DefinePlugin
允许你在编译时定义全局的常量,这些常量会在代码中被替换为指定的值,常用于配置不同的环境变量(如开发环境、生产环境等)。
-
mini-css-extract-plugin
将 CSS 代码从 JavaScript 文件中提取出来,生成独立的
.css文件,这样可以更好地利用浏览器的缓存机制,提高应用的性能。
-
html-webpack-inject-attributes-plugin
主要作用是为
html-webpack-plugin生成的 HTML 文件中的资源标签(如<script>和<link>)注入自定义属性。
-
css-minimizer-webpack-plugin
用于压缩 CSS 代码,减少文件大小,提高应用的加载速度。
代码分割
代码分割的核心思想是将代码分成多个逻辑块,每个块可以独立加载。这样,用户在访问应用时,只需加载当前需要的代码,而不是整个应用的所有代码。这可以显著减少初始加载时间,提高应用的响应速度。
-
分包策略配置的核心
- 把 js 文件打包成3中类型
- vendor:第三方 lib 库,基本不会改动,除非依赖版本升级
- common:业务组件代码的公共部分抽取出来,改动较少
- entry.{page}:不同页面 entry 里的业务组件代码的差异部分,会经常改动
- 目的:把改动和应用频率不一样的 js 区分出来,以达到更好利用浏览器缓存的效果
// 配置打包输出优化(配置代码分割,模块合并,缓存,TreeShaking,压缩等优化策略)
optimization: {
splitChunks: {
chunks: 'all', // 对同步和异步模块都进行分割
maxAsyncRequests: 10, // 每次异步加载的最大并行请求数
maxInitialRequests: 10, // 入口点的最大并行请求数
cacheGroups: {
vendor: { // 第三方依赖库
test: /[\\/]node_modules[\\/]/, // 打包 node_modules 中的文件
name: 'vendor', // 模块名称
priority: 20, // 优先级,数字越大,优先级越高
enforce: true, // 强制执行
reuseExistingChunk: true, // 复用已有的公共 chunk
},
common: { // 公共模块
name: 'common', // 模块名称
minChunks: 2, // 被两处引用即被归为公共模块
minSize: 1, // 最小分割文件大小(1 byte)
priority: 10, // 优先级
reuseExistingChunk: true, // 复用已有的公共 chunk
}
}
},
// 将 webpack 运行时生成的代码打包到 runtime.js
runtimeChunk: true
}
热更新(HMR)
它允许在不刷新整个页面的情况下,实时替换、添加或删除代码模块(如 JavaScript、CSS、组件),并保持当前应用状态(如输入框内容、UI 状态)不变。这一技术极大提升了开发效率和调试体验。
-
技术实现
- HotModuleReplacementPlugin:启用热模块替换功能,允许在运行时更新模块。
- webpack-dev-middleware:提供实时编译和内存存储功能,避免每次修改文件后都需要重新编译。
- webpack-Hot-middleware:与
webpack-dev-middleware配合使用,实现浏览器的无刷新更新。
// 生产环境 webpack 配置
const webpackConfig = merge.smart(baseConfig, {
// 指定开发环境模式
...
// 开发阶段 output 配置
...
// 开发阶段插件
plugins: [
// HotModuleReplacementPlugin 用于实现热模块替换 (Hot Module Replacement 简称 HMR)
// 模块热替换允许在应用程序运行时替换模块
// 极大的提升开发效率,因为能让应用程序一直保持运行状态
new webpack.HotModuleReplacementPlugin({
multiple: false
})
]
});
// 引用 divMiddleware 中间件(监控文件改动)
app.use(devMiddleware(compiler, {
// 落地文件
writeToDisk: (filePath) => filePath.endsWith('.tpl'),
// 资源路径
publicPath: webpackConfig.output.publicPath,
// header 配置
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
'Access-Control-Allow-Headers': 'X-Requested-Width, content-type, Authorization'
},
stats: {
colors: true
}
}));
// 引用 hotMiddleware 中间件(实现热更新通讯)
app.use(hotMiddleware(compiler, {
path: `/${DEV_SERVER_CONFIG.HMR_PATH}`,
log: () => { }
}));
总结
前端工程化的最终目标是让前端开发更加高效、可靠和可持续,以适应不断变化的需求和日益复杂的项目。随着技术的发展,前端工程化的方法和工具也在不断演进,需要开发者持续学习和实践。