webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个依赖图,然后将你项目中所需的每一个模块组合成一个或多个 bundles*,它们均为静态资源,用于展示你的内容。
本文将从如下最基础的项目入手,介绍 webpack 的一些基本概念。
一、入口(entry)
官方解释: 入口起点(entry point) 指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。
默认值是
./src/index.js
,可以通过在webpack.config.js
文件中配置entry
属性,来指定一个(或多个)不同的入口起点
1.1 基本语法:entry: string | [string]
- 单入口:当只有单个入口时,可以按如下方式简写:
module.exports = {
entry: './path/to/my/entry/file.js',
};
- 多入口:可以将一个文件路径数组传递给
entry
属性,这将创建一个所谓的 “multi-main entry”
module.exports = {
entry: ['./src/file_1.js', './src/file_2.js'],
output: {
filename: 'bundle.js',
},
};
1.2 对象语法:entry: { <entryChunkName> string | [string] } | {}
module.exports = {
entry: {
main: './path/to/my/entry/file.js',
},
};
其实上述第一种写法就是这种写法的一种简写形式。
对于每个入口我们可以使用如下属性进行描述:
filename
: 指定要输出的文件名称。即入口文件。dependOn
: 当前入口所依赖的入口。它们必须在该入口被加载前被加载。import
: 启动时需加载的模块。library
: 指定 library 选项,为当前 entry 构建一个 library。runtime
: 运行时 chunk 的名字。如果设置了,就会创建一个新的运行时 chunk。publicPath
: 当该入口的输出文件在浏览器中被引用时,为它们指定一个公共 URL 地址。
module.exports = {
entry: {
a2: 'dependingfile.js',
b2: {
dependOn: 'a2',
import: './src/app.js',
},
},
};
二、输出(output)
output 属性告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件。主要输出文件的默认值是
./dist/main.js
,其他生成文件默认放置在./dist
文件夹中。
output的基础配置如下:
const path = require('path');
module.exports = {
entry: './path/to/my/entry/file.js',
output: {
// 输出文件夹
path: path.resolve(__dirname, 'dist'),
// 输出文件名
filename: 'bundle.js',
},
};
其中需要引入node.js
的path
模块对路径进行处理,其中path.resolve
是将一系列路径或路径段解析为绝对路径。所以说 path
的属性值一定要是绝对路径。
当通过多个入口起点(entry point)、代码拆分(code splitting)或各种插件(plugin)创建多个 bundle,应该使用以下一种替换方式,来赋予每个 bundle 一个唯一的名称。
- 使用入口名称
module.exports = {
//...
output: {
filename: '[name].bundle.js',
},
};
- 使用内部chunk id
module.exports = {
//...
output: {
filename: '[id].bundle.js',
},
};
- 使用内容产生的hash
module.exports = {
//...
output: {
filename: '[contenthash].bundle.js',
},
};
当然output中还有很多配置属性,这里就不一一介绍了。
三、loader
webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。
loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中。
在 webpack 的配置中,loader 有两个属性:
test
属性,识别出哪些文件会被转换。use
属性,定义出在进行转换时,应该使用哪个 loader
如下:
module.exports = {
...
module: {
rules: [
{
test: /.txt$/,
use: 'raw-loader'
}
],
},
...
};
该配置告诉 webpack 在打包时,如果遇到 import/require() 语句中存在解析为.txt
的路径时,在打包之前,先用 raw-loader
进行转换。
我们也可以针对不同的类型的资源,配置不同的loader:
module.exports = {
module: {
rules: [
{ test: /.css$/, use: 'css-loader' },
{ test: /.ts$/, use: 'ts-loader' },
],
},
};
module.rules
允许你在 webpack 配置中为某个类型资源指定多个 loader,以加载 CSS 文件为例:
module.exports = {
module: {
rules: [
{
test: /.css$/,
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
modules: true
}
},
]
}
]
}
};
- loader 支持链式调用(从右往左 或 从下往上)。链中的每个 loader 会将转换应用在已处理过的资源上。一组链式的 loader 将按照相反的顺序执行。链中的第一个 loader 将其结果(也就是应用过转换后的资源)传递给下一个 loader,依此类推。最后,链中的最后一个 loader,返回 webpack 所期望的 JavaScript。例如:上例中 CSS 文件会先被
css-loader
处理,处理后的资源紧接着传给style-loader
处理。 - loader 可以是同步的,也可以是异步的。
- loader 运行在 Node.js 中,并且能够执行任何操作。
- loader 可以通过
options
对象配置。 - loader 能够产生额外的任意文件。
四、插件(plugin)
插件可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。
webpack 插件是一个具有
apply
方法的 JavaScript 对象。apply
方法会被 webpack compiler 调用,并且在 整个 编译生命周期都可以访问 compiler 对象。
想要使用一个插件,你只需要 require()
它,然后把它添加到 plugins
数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new
操作符来创建一个插件实例
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack'); // 用于访问内置插件
module.exports = {
module: {
rules: [{ test: /.txt$/, use: 'raw-loader' }],
},
plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })],
};
在上面的示例中,html-webpack-plugin
为应用程序生成一个 HTML 文件,并自动将生成的所有 bundle 注入到此文件中。
五、模式(mode)
设置 mode
参数,你可以启用 webpack 内置在相应环境下的优化。可以设置为 development
, production
或 none
其中一个,默认值为production
。
选项 | 描述 |
---|---|
development |
会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 development . 为模块和 chunk 启用有效的名。 |
production |
会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 production 。为模块和 chunk 启用确定性的混淆名称,FlagDependencyUsagePlugin ,FlagIncludedChunksPlugin ,ModuleConcatenationPlugin ,NoEmitOnErrorsPlugin 和 TerserPlugin 。 |
none |
不使用任何默认优化选项 |
简单来说,在production
模式下,webpack会进行更加细致的打包优化,例如代码压缩等。不同模式下会有不同的配置。
上述入口(entry)、输出(output)、loader、插件(plugin)、模式(mode)是webpack的核心概念。了解了这些,我们在配置文件中进行如下简单的配置:
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js',
},
mode: 'development'
}
然后在入口文件 ./src/index.js 中写入如下代码:
function f(a, b) {
return a + b;
}
然后再命令行运行 webpack 进行打包,可以看到 dist 目录下会生成 main.js,内容如下图:
六、模块(Modules)
在模块化编程中,开发者将程序分解为功能离散的 chunk,并称之为 模块。对于 webpack 而言,天生支持以下模块类型:
- ECMAScript 模块
- CommonJS 模块
- AMD 模块
- Assets
- WebAssembly 模块
通过 loader 可以使 webpack 支持多种语言和预处理器语法编写的模块。loader 向 webpack 描述了如何处理非原生模块,并将相关依赖引入到你的 bundles中。
webpack 能够解析三种文件路径:
- 绝对路径
import 'C:\Users\me\file';
此种情况下不需要进行进一步解析。
- 相对路径
import './file2';
在这种情况下,使用 import
或 require
的资源文件所处的目录,被认为是上下文目录。在 import/require
中给定的相对路径,会拼接此上下文路径,来生成模块的绝对路径。
- 模块路径
import 'module';
该种路径与配置项 resolve 相关,这里暂不详细描述。
七、依赖图
当 webpack 处理应用程序时,它会根据命令行参数中或配置文件中定义的模块列表开始处理。 从入口开始,webpack 会递归的构建一个 依赖关系图,这个依赖图包含着应用程序中所需的每个模块,然后将所有模块打包为少量的 bundle —— 通常只有一个 —— 可由浏览器加载
八、runtime 和 manifest
runtime
runtime,以及伴随的 manifest 数据,主要是指:在浏览器运行过程中,webpack 用来连接模块化应用程序所需的所有代码。它包含:在模块交互时,连接模块所需的加载和解析逻辑。包括:已经加载到浏览器中的连接模块逻辑,以及尚未加载模块的延迟加载逻辑。
manifest
一旦你的应用在浏览器中以 index.html
文件的形式被打开,一些 bundle 和应用需要的各种资源都需要用某种方式被加载与链接起来。在经过打包、压缩、为延迟加载而拆分为细小的 chunk 这些 webpack 优化之后,你精心安排的 /src
目录的文件结构都已经不再存在。所以 webpack 如何管理所有所需模块之间的交互呢?这就是 manifest 数据用途的由来……
当 compiler 开始执行、解析和映射应用程序时,它会保留所有模块的详细要点。这个数据集合称为 “manifest”,当完成打包并发送到浏览器时,runtime 会通过 manifest 来解析和加载模块。无论你选择哪种 模块语法,那些 import
或 require
语句现在都已经转换为 __webpack_require__
方法,此方法指向模块标识符(module identifier)。通过使用 manifest 中的数据,runtime 将能够检索这些标识符,找出每个标识符背后对应的模块。
以上内容均参考 webpack 官方文档,详细内容可见官方文档。
参考:
webpack文档
原文链接:https://juejin.cn/post/7261059812238983229 作者:大力yy