Webpack基础
一、什么是Webpack
Webpack 是一个用于构建前端应用的工具,它将多个模块打包成一个或多个文件,并提供了代码拆分、懒加载、模块热替换等功能。
webpack 的模块是什么
Webpack 忽略所有资源之间的差异,将所有代码/非代码文件都统一看作 Module —— 模块对象。
这些模块都以相同的加载、解析、依赖管理、优化、合并流程实现打包,就像 webpack 官网所示这样。
并借助 Loader、Plugin 两种开放接口将资源差异处理逻辑转交由社区实现,实现统一资源构建模型
这种设计有很多优点:
- 所有资源都是 Module,所以可以用同一套代码实现诸多特性,包括:代码压缩、Hot Module Replacement、缓存等
- 打包时,资源与资源之间非常容易实现信息互换,例如可以轻易在 HTML 插入 Base64 格式的图片
- 借助 Loader,Webpack 几乎可以用任意方式处理任意类型的资源,例如可以用 Less、Stylus、Sass 等预编译 CSS 代码
为什么要模块打包呢?
1. 浏览器不识别CommonJS等模块化规范
要知道node.js 生态中积累了大量的 JavaScript 写的代码,却因为 node.js 端遵循的 CommonJS 模块化规范与浏览器端格格不入,导致代码无法得到复用,这是一个巨大的损失。于是 webpack 要做的就是将这些模块打包成游览器识别的代码。
2. 依赖引入顺序问题
在不使用webpack的情况下,我们引入JS文件是这样的
// index.html
<script src="index.js"></script>
但是如果一个页面需要引入多个模块,那么就需要写成这样
// index.html
<script src="index1.js"></script>
<script src="index2.js"></script>
<script src="index3.js"></script>
虽然这样引入初看还可以,但是事实却隐藏着一个问题,那就是如何保证依赖执行的顺序正确?
如果 index1.js 使用到了 index2.js 的内容就会报错。这时因为JS执行时从上到下的,所以在执行 index1.js index2.js还没执行,里面的内容也还没有。
webpack 如何识别模块关系
- 首先 webpack 会从你配置的入口文件开始
- 寻找当前文件所依赖的模块
- 查找 2 中导入模块的依赖,进行遍历递归,重复 1 2 3 顺序执行
- 直到查找完全部模块,生成依赖图
二、webpack 工作流程
webpack 的工作流程可以概括为下面四个步骤:
其中 Loader 主要功能是处理 webpack 获取到的模块,webpack 自身只会识别 js 模块。所以各种 webpack 不能识别的资源都将交给 loader 处理。
而 Plugin 则是对于 Loader 处理出来的资源进行进一步的改造,例如 代码压缩、生成 HTML 文件等。
最终输出所有文件和资源 boudle
三、webpack 配置
Entry 入口文件
Webpack 的入口起点是通过配置文件指定的一个或多个 JavaScript 文件,这些文件会作为打包的起点。Webpack 会根据这些入口文件来分析应用中的模块依赖关系,构建依赖图。
// webpack.config.js
module.exports = {
entry: './index.js'
};
Loader 模块解析
Webpack 使用加载器(Loader)来处理非 JavaScript 和 JSON 类型的模块:将其转换成 JavaScript 模块,然后再进行打包。Webpack 支持多种类型的加载器,例如 css-loader 等。
在 webpack 的配置中,loader 有两个属性:
- test 属性,识别出哪些文件会被转换。
- use 属性,定义在进行转换时,应该使用哪个 loader。
// webpack.config.js
module.exports = {
module: {
rules: [{ test: /\.txt$/, use: 'raw-loader' }],
},
}
以上 loader 的意思是告诉 webpack 在碰到 require()/import 语句中被解析为 '.txt' 的路径时,在你对它打包之前,先 use(使用) raw-loader 转换一下。
TIP
像 png、jpg 等基础资源的识别,webpack5 内置资源模块(asset module)。不需要安装额外的 loader模块 (file-loader 等)来提供帮助,只需要配置 type 即可 { test: /.png/, type: 'asset/resource' }。
Plugin 插件
Webpack 使用插件(Plugin)来扩展其功能,例如压缩代码、提取公共代码、生成 HTML 文件等。开发者可以编写自己的插件来满足特定需求。
HtmlWebpackPlugin(自动生成一个 HTML 文件)
安装 HtmlWebpackPlugin
npm install HtmlWebpackPlugin -D
配置 webpack.config.js
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
module.exports = {
entry: './index.js',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'index.js',
},
plugins: [new HtmlWebpackPlugin()],
}
想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new 操作符来创建一个插件实例。
Output 输出结果
Webpack 将所有模块打包成一个或多个文件,并且可以生成 sourcemap 文件用于调试。输出的文件可以通过配置文件指定其名称、路径和格式。
// webpack.config.js
const path = require('path');
module.exports = {
entry: './index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js',
},
};
四、其它配置补充
asset module
资源模块(asset module)是一种模块类型,它允许使用资源文件(字体,图标等)而无需配置额外 loader。 在 webpack 5 之前,通常使用:
- raw-loader 将文件导入为字符串
- url-loader 将文件作为 data URI 内联到 bundle 中
- file-loader 将文件发送到输出目录
资源模块类型(asset module type),通过添加 4 种新的模块类型,来替换所有这些 loader:
- asset/resource 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现。
- asset/inline 导出一个资源的 data URI。之前通过使用 url-loader 实现。
- asset/source 导出资源的源代码。之前通过使用 raw-loader 实现。
- asset 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源体积限制实现。
例如:
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.png/,
type: 'asset/resource'
}
]
},
}
所有 .png 文件都将被发送到输出目录,并且其路径将被注入到 bundle 中,除此之外,你可以为它们自定义 outputPath 和 publicPath 属性。
optimization
用于控制如何优化产物包体积,内置 Dead Code Elimination、Scope Hoisting、代码混淆、代码压缩等功能
module.exports = {
//...
optimization: {
//...
};
}
详细配置请看 JS 优化。
mode
配置 webpack 的模式,有三个值:
- development 开发模式,会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 development。
- production 生产模式,会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 production。
- none 不使用任何默认优化选项
module.exports = {
mode: 'production',
}
如果没有设置,默认为 production。在 webpack5 中使用 production 生产模式,回默认开启一些优化:例如启动 JS 代码压缩等优化。
devServer
用于拉起一个服务来启动前端项目
module.exports = {
devServer: {
contentBase: path.join(__dirname, 'dist'),
port: 8080
}
}
- contentBase:告诉服务器内容的来源。
- port:设置服务器端口,默认8080
五、认识 Module、Chunk 和 Bundle
Module
Module(模块)是Webpack中最基本的概念,它代表着一个单独的文件(或一组相关的文件),它可以是JavaScript、CSS、图片、JSON等任何类型的文件。在Webpack中,每个Module都会被转换成一个或多个Chunk。
Chunk
Chunk(代码块)是Webpack打包过程中的中间产物,它代表着一组被合并在一起的Modules。通常情况下,Chunk是由多个Module组成的,Webpack会根据一定的规则将这些Module打包成一个Chunk。Chunk可以被进一步处理和优化,最终被合并成Bundle。
Bundle
Bundle(捆绑包)是Webpack最终输出的文件,它是由一组已经经过加载和编译的Chunk组成的。通常情况下,一个应用程序会生成一个Bundle,它包含了所有的JavaScript、CSS、图片等资源,可以被直接加载到浏览器中运行。
Module是Webpack中最小的单元,它们组成了Chunk,而Chunk则最终被合并成Bundle。module,chunk 和 bundle 其实就是同一份逻辑代码在不同转换场景下的取了三个名字