Webpack 核心概念
webpack 是一个用于现代 JavaScript 应用程序的静态模块打包工具。当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个 依赖图(dependency graph),然后将你项目中所需的每一个模块组合成一个或多个 bundles,它们均为静态资源,用于展示你的内容。
webpack 核心概念包含一下几点:
- entry(入口)
- output(输出)
- loader(加载器)
- plugin(插件)
- mode(模式)
- browser compatibility(浏览器兼容性)
- environment(环境)
entry(入口起点)
entry(入口)配置属性支持多种类型:
- 字符串:表示单个入口文件的路径。
- 数组:表示多个入口文件。
- 对象:可以包含多个入口点,每个入口点都有自己的键和值。
- 对象形式的高级用法:支持更复杂的结构,例如嵌套对象或函数。
基础配置
字符串配置
module.exports = {
entry: './path/to/my/entry/file.js',
}
数组配置
module.exports = {
entry: ['./src/file_1.js', './src/file_2.js'],
}
对象配置
module.exports = {
entry: {
app: './src/app.js',
adminApp: './src/adminApp.js',
},
}
高级配置
函数配置
动态入口
app 入口点的路径是通过一个函数动态返回的,根据不同的条件或环境变量来确定实际的入口文件。
module.exports = {
entry: {
app: () => {
// 动态决定入口文件
return path.resolve(__dirname, 'src', 'app.js')
},
// ...
},
}
异步加载模块
asyncModules 入口点使用了动态 import 语法,允许模块异步加载。
module.exports = {
entry: {
app: './src/app.js',
styles: './src/styles.css',
// 异步加载模块
asyncModules: () => import('./src/asyncModules.js'),
},
}
条件性入口
conditional 入口点根据环境变量 SOME_FLAG 的值来决定是否包含入口文件。
module.exports = {
entry: {
app: './src/app.js',
styles: './src/styles.css',
// 异步加载模块
asyncModules: () => import('./src/asyncModules.js'),
},
}
对象属性配置
对象配置属性:
- import:启动时需加载的模块。
- dependOn:当前入口所依赖的入口。它们必须在该入口被加载前被加载。
- alias:入口别名。为入口点指定别名,这可以在其他地方引用该入口时使用。
- priority:入口优先级。值越大优先级越高,webpack 先加载优先级高的模块。
- plugins:入口插件。为入口指定所需要的插件。
- runtime:运行时 chunk 的名字。如果设置了,就会创建一个新的运行时 chunk。在 webpack 5.43.0 之后可将其设为 false 以避免一个新的运行时 chunk。
配置参考示例:
共享依赖入口
module.exports = {
entry: {
app: {
import: './src/app.js',
dependOn: 'shared',
},
shared: 'lodash',
},
}
分离第三方库
module.exports = {
entry: {
app: './src/app.js',
vendor: ['react', 'react-dom'],
},
}
入口别名
module.exports = {
entry: {
app: {
import: './src/app.js',
// 为入口指定别名
alias: 'main-app',
},
},
}
入口优先级
module.exports = {
entry: {
app: {
import: './src/app.js',
// 设置入口的优先级
priority: 10,
},
other: {
import: './src/other.js',
priority: 5,
},
},
}
入口插件
module.exports = {
entry: {
app: {
import: './src/app.js',
// 为入口指定插件
plugins: [new MyCustomPlugin()],
},
},
}
output(输出)
可以通过配置 output 选项,告知 webpack 如何向硬盘写入编译文件。注意,即使可以存在多个 entry 起点,但只能指定一个 output 配置。
常用配置属性说明
- filename:指定输出文件的名称。
- path:指定输入文件的目录,必须是绝对路径。
- publicPath:指定资源的公共路径,即在 HTML 文件中引用这些资源时的路径。
- chunkFilename:指定额外的 chunk 文件的名称模板。通常用于代码分割时生成的文件。
- library:当使用 output.library 时,Webpack 会导出一个全局变量,该变量可以被浏览器脚本访问。
- libraryTarget:指定库的目标类型。例如,var 会将库导出为全局变量。
- clean:设置为 true 时,Webpack 会在每次构建前清理输出目录。
- crossOriginLoading:设置跨域加载的行为。例如,设置为 anonymous 时,资源将以匿名方式加载。
- devtoolModuleFilenameTemplate:当使用 source map 时,此选项允许自定义源文件的路径。
- globalObject:指定全局对象的名称,通常用于 Node.js 环境。
const path = require('path')
module.exports = {
output: {
// [name]占位符,它会被替换为入口点的名称
filename: '[name].bundle.js',
// 指定输出文件的目录
path: path.resolve(__dirname, 'dist'),
// 指定资源公众路径
publicPath: '/assets/',
// 指定额外的chunk文件的名称
chunkFilename: '[id].chunk.js',
// 指定JS导出的包的全局变量,打包库、框架时使用
library: 'MyLibrary',
// 指定库的目标类型,打包库、框架时使用
libraryTarget: 'var',
// 设置为anonymous时,资源将以匿名方式加载。
crossOriginLoading: 'anonymous',
// 指定全局对象的名称,通常用于Node.js环境。
globalObject: 'this',
},
}
loader(加载器)
loader 用于对模块的源代码进行转换。loader 可以使你在 import 或 "load(加载)" 模块时预处理文件。因此,loader 类似于其他构建工具中“任务(task)”,并提供了处理前端构建步骤的得力方式。loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript 或将内联图像转换为 data URL。loader 甚至允许你直接在 JavaScript 模块中 import CSS 文件!
loader 使用
应用程序中,有两种使用 loader 的方式:
- 配置方式(推荐):在 webpack.config.js 文件中指定 loader。
- 内联方式:在每个 import 语句中显式指定 loader。
配置方式使用 loader
module.rules 允许你在 webpack 配置中指定多个 loader。 这种方式是展示 loader 的一种简明方式,并且有助于使代码变得简洁和易于维护。
注意:loader 在配置中从下到上(或从右到左)取值执行;下面的示例中从 sass-loader 开始执行,然后继续执行 css-loader,最后以 style-loader 为结束。
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
modules: true,
},
},
{ loader: 'sass-loader' },
],
},
],
},
}
内联方式使用 loader
不推荐使用,不展示讲解
import Styles from 'style-loader!css-loader?modules!./styles.css'
loader 工作原理
Loaders 本质上是 Node.js 模块,它们接收源文件的内容作为输入,并返回转换后的结果。这个过程可以通过同步或异步的方式完成。Loaders 可以链式调用,每个 Loader 处理特定的转换任务,最终将源文件转换为 JavaScript 模块。
loader 特性
- loader 支持链式调用。链中的每个 loader 会将转换应用在已处理过的资源上。一组链式的 loader 将按照相反的顺序执行。链中的第一个 loader 将其结果(也就是应用过转换后的资源)传递给下一个 loader,依此类推。最后,链中的最后一个 loader,返回 webpack 所期望的 JavaScript。
- loader 可以是同步的,也可以是异步的。
- loader 运行在 Node.js 中,并且能够执行任何操作。
- loader 可以通过 options 对象配置(仍然支持使用 query 参数来设置选项,但是这种方式已被废弃)。
- 除了常见的通过 package.json 的 main 来将一个 npm 模块导出为 loader,还可以在 module.rules 中使用 loader 字段直接引用一个模块。
- 插件(plugin)可以为 loader 带来更多特性。
- loader 能够产生额外的任意文件。
plugin(插件)
Webpack Plugins 是 Webpack 构建系统中的扩展点,它们可以在构建过程中执行各种任务,如打包优化、资源管理和环境变量注入等。
插件目的在于解决 loader 无法实现的其他事。
插件剖析
webpack 插件是一个具有 apply 方法的 JavaScript 对象。apply 方法会被 webpack compiler 调用,并且在 整个 编译生命周期都可以访问 compiler 对象。
const pluginName = 'TestWebpackPlugin'
class TestWebpackPlugin {
config = {}
constructor(config) {
this.config = config
}
apply(compiler) {
compiler.hooks.run.tap(pluginName, (compilation) => {
console.log('webpack 构建正在启动!')
console.log('参数:', this.config)
})
}
}
module.exports = TestWebpackPlugin
插件使用
由于插件可以携带参数/选项,你必须在 webpack 配置中,向 plugins 属性传入一个 new 实例。
const HtmlWebpackPlugin = require('html-webpack-plugin')
const webpack = require('webpack') // 访问内置的插件
const path = require('path')
module.exports = {
entry: './path/to/my/entry/file.js',
output: {
filename: 'my-first-webpack.bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: 'babel-loader',
},
],
},
plugins: [
new webpack.ProgressPlugin(),
new HtmlWebpackPlugin({ template: './src/index.html' }),
],
}
示例中:ProgressPlugin 用于自定义编译过程中的进度报告,HtmlWebpackPlugin 将生成一个 HTML 文件,并在其中使用 script 引入一个名为 my-first-webpack.bundle.js 的 JS 文件。
常用插件
- HtmlWebpackPlugin:自动生成 HTML 文件,并将打包后的 JavaScript 文件注入到这些 HTML 文件中。
- MiniCssExtractPlugin:将 CSS 提取到单独的文件中,而不是在 JavaScript 中以
- UglifyJsPlugin:压缩 JavaScript 文件,移除无用的代码,减少文件大小。
- TerserPlugin:替代 UglifyJsPlugin 的压缩工具,提供更好的压缩效果和性能。
- DefinePlugin:允许创建预定义的全局常量,这些常量可以在编译时被替换。
- CopyWebpackPlugin:复制单个文件或整个目录到构建目录。
- CleanWebpackPlugin:在每次构建之前清除输出目录。
- ProgressPlugin:显示构建进度。
- HotModuleReplacementPlugin:启用模块热替换(HMR),允许在不刷新页面的情况下更新模块。
mode(模式)
提供 mode 配置选项,告知 webpack 使用相应模式的内置优化。
string = 'production': 'none' | 'development' | 'production'
用法
配置形式:
module.exports = {
mode: 'development',
}
命令形式:
webpack --mode=development
参数选项说明
- development: 开发模式,启用快速反馈循环和优化的开发体验。
- production: 生产模式,启用最佳实践的生产构建,包括代码分割、压缩和其他优化措施。
- none: 不设置任何模式,Webpack 将不会应用任何与模式相关的优化。
如果不设置 mode,Webpack 会默认使用 production 模式。然而,为了确保正确的优化和警告信息,建议显式设置 mode。
browser compatibility(浏览器兼容性)
Webpack 支持所有符合 ES5 标准 的浏览器(不支持 IE8 及以下版本)。webpack 的 import() 和 require.ensure() 需要 Promise。如果你想要支持旧版本浏览器,在使用这些表达式之前,还需要 提前加载 polyfill。
environment(环境)
Webpack 5 运行于 Node.js v10.13.0+ 的版本。