今天小编给大家带来一篇社区作者limingcan的文章。 让我们学习一下使用webpack涉及到的几个概念,以便我们更好的理解webpack编译过程。
前言
随着vite的诞生,webpack似乎慢慢被你抛弃了。 前段时间我也用vue@3.x + vite@4.x开发了一个后台管理系统,体验了一下,着实被vite快速的启动速度震惊了。
不过webpack虽然诞生已经很久了,但也经过了市场的一些考验,而且插件丰富,功能丰富,一些小项目也使用了它。 目前是比vite更稳定的打包工具。
出于以下原因:
所以,想来想去,我决定开一个webpack专栏,和大家一起学习如何配置webpack(虽然专栏有点晚了)。
专栏将带您一步步学习webpack,从更多的实际例子开始,让您更好地理解webpack,最终自己准备一个完整的webpack脚手架。 当我们学会自己配置webpack后,我们可以根据自己的不同需求高度定制自己的脚手架,还是很实用的。
本文的主要核心就是和大家一起了解一下使用webpack所涉及到的概念,以便我们更好的理解webpack的编译过程。
什么是网页包
它是一个打包工具:
webpack.config.js
v4.0.0之后,webpack有了一个开箱即用的功能,就是我们安装了webpack和webpack-cli这两个依赖之后,就可以直接打包,不需要任何配置文件。
这是因为:
但我们通常会在项目根目录中创建一个名为 webpack.config.js 的新配置文件。 这可以指导webpack如何处理我们项目中的文件、资源等; 通过更加高度定制的配置来满足我们项目的需求。
我们看一下这个配置文件的大致框架是什么样的,心里有一个印象:
// webpack.config.js
module.exports = {
// 入口
entry: {},
// 打包输出
output: {},
// 配置模块如何解析
resolve: {},
// 配置各种loader
module: {},
// 配置插件
plugins: [],
// 优化(可以进行代码分割)
optimization: {},
// webpack-dev-server 开发时的配置,一般用于development模式
devServer: {}
};
其实webapck也支持很多配置项,不过这些是我们平时使用的,其他的配置项可以在文档中找到。
注意
webpack配置文件,里面的代码你想玩什么就玩什么,配置文件的名字不一定非得是webpack.config.js,只要最终输出的是一个webpack配置对象即可:
// myconfig.js
// 各种逻辑代码
...
...
// 保证最后导出一个`webpack`配置的对象就可以
module.exports = {
// 各种webpack配置
entry: {},
output: {},
....
}
"build": "webpack --config myconfig.js"
优化
所有配置都写在一个文件中,不利于我们维护和开发; 而我们开发项目时,会存在多种环境(本地开发环境、测试环境、生产环境)webpack模块分割,所以我们通常会有针对不同环境的配置文件。 所有环境的通用配置文件。
我们通常会将“通用配置文件”与“不同环境的配置文件”进行合并,最后通过webpack运行合并后的配置文件。
配置文件通常有:
后续文章将教您如何进行这些配置
入口
它指的是webpack开始解析并构建依赖图的起点。 我们通常使用 {key: value} 对象来配置条目,例如:
module.exports = {
entry: {
index: './src/index.js',
share: './src/share.js',
},
}
这意味着:
输出
它很容易理解,它可以设置webpack打包的编译文件的名称,以及应该输出的位置。
需要注意的是,即使我们设置了多个entry条目,也只能指定一种输出配置。
模块
我们导入或者需要的资源文件,或者说我们项目中的每一个文件,都可以看成是一个独立的模块。 因此,它可以是任何类型的资源,例如js文件、css文件或图片。 所以我们在开发中经常会听到以下这句话:
模块的组合会生成一个Chunk。 Chunk是一个非常重要的概念,后面会详细讨论。
参考文章:模块 (#what-is-a-webpack-module)
装载机
通过上面我们知道,项目中的每个文件都可以看成是一个模块,而由于我们的文件类型多种多样,所以我们需要有一个东西可以把这个模块解析成webpack可以识别的有效模块,并将其添加到依赖图,这个东西就是Loader。
webpack还好心地为我们提供了一个模块配置项,专门用来配置不同的loader,从而解析不同的文件类型(模块)。 这是因为webpack默认只能解析js和json文件,所以如果我们要解析不同类型的文件(模块),就需要安装相应的loader:
// webpack.config.js
module.exports = {
...,
modules: {
rules: [
// 解析.txt文件,使用raw-loader
{ test: /.txt$/, use: 'raw-loader' },
],
}
}
加载器有两个属性:
在webpack中,还有哪些其他加载器可以在这里看到()
插件
Loader用于转换模块,Plugin用于打包编译时增强webpack的功能。 所以它通常具有打包优化、资源管理、环境变量注入等功能。
因此,webpack还贴心地为我们提供了插件配置项。 我们想要完善哪些功能都可以在插件中进行配置。 比如我们项目中直接定义一些全局变量:
// webpack.config.js
module.exports = {
...,
plugins: [
new webpack.DefinePlugin({
AUTHOR_NAME: JSON.stringify('Lee'),
})
]
}
// index.js
console.log(AUTHOR_NAME); // Lee
webpack中还有哪些插件可以看这里()
依赖图(依赖图)
当我们有一个文件依赖于另一个文件时,webpack 会将该文件视为直接“依赖项”。
举个简单的例子,假设我们有三个文件,它们的关系如下:
// main.js
import './index.js';
// index.js
import 'plugin.js'
console.log('I am from index.js');
// plugin.js
console.log('I am plugin.js');
我们来剖析一下:
main.js、index.js、plugin.js相当于三个模块;
然后main.js引入了index.js,index.js又引入了plugin.js;
因此,这三个文件都具有相互引用的关系;
这时就生成了这样一张有引用关系的图:main.js→index.js→plugin.js
总结:
webpack将从入口开始,并将其作为依赖图的起点; 然后对entry中的内部导入进行分析处理,不断递归查询类似上面例子的依赖关系。 这个过程最终会生成一个具有依赖关系的图。 这个图是一个“依赖图”。
webpack 将遵循这个依赖图进行进一步的操作。
块
我们在查阅webpack中文文档的时候,经常会听到Chunk这个词,这个词还没有翻译成英文。 这是因为Chunk是webpack打包过程中形成的逻辑概念,其含义需要结合上下文来理解。
Chunk是webpack中一个比较重要的概念。 如果我们理解了它,我们就会很好地理解webpack打包和代码分割的整个过程。
解释
在webpack打包过程中,一个或者一组模块(我们上面提到的webpack中的任何一个文件都可以看作是一个模块)会被组合成一个整体,那么这个整体就可以看作是一个Chunk。 一般来说,webpack在打包过程中,会产生若干个Chunk,最终会输出若干个js文件。
我们以learn-01为例,它包含最简单的配置和最简单的代码,以便更好地理解Chunk是什么。
我们有三个js文件:
文件代码和webpack配置如下:
// webpack.config.js
module.exports = {
entry: {
index: './src/index.js'
},
output: {
filename: '[name]-bundle.js'
}
}
// index.js
import './vendors';
import(/* webpackChunkName: "async" */ './async');
console.log('I am from index.js');
// async.js
console.log('I am from async.js');
// vendors.js
console.log('I am from vendors.js');
看到这里,我们首先可以猜测一下打包了多少个文件。 如果你猜对了,说明你对Chunk有了一定的了解。
分析
我们先来分析一下打包后的文件和结构:
dist
├── async-bundle.js
└── index-bundle.js
总共输出两个js文件。
通过上述,我们知道:
我们接着分析:
好了,相信到这里,大家应该对Chunk有了一个大概的印象了,我们再回顾一下整个过程。
编译webpack时,通过我们的配置:
首先会找到Enrty,如果有多个条目webpack模块分割,则会与该条目组成一个Chunk组(示例中为Chunk[index])
然后分析这种Chunk组,与入口js以及该入口的所有相关依赖模块组成一个chunk(例子中的chunk[initial])
如果有异步引入的模块,该模块会形成一个Chunk(示例中为chunk[no-initial])
最后打包输出 chunk[initial] (index-bundle.js), chunk[no-initial] (async-bundle.js)
上面的例子中,chunk和module的关系如下:
形式
通过上面的分析,我们可以知道Chunk有两种类型:
如何形成一个 chunk
通过前面的讲解和分析,希望大家以后使用webpack的时候,能够在脑海里有一个大概的Chunk形成的过程,这对于我们使用代码分割是非常有帮助的。
参考文章:揭示内部原理()
捆
Bundle是指所有通过webpack打包的产品。
如果我们的输出配置打包后的输出文件目录是dist,那么我们的Bundle就是dist文件夹上面的所有产品。
一般来说有几个Chunk,就会打包多少个js的bundle文件。
包装工艺
分析
为了让大家更好的理解上面分析的概念,我们来探究一下webpack的打包流程,看看上面的概念是在什么过程中体现的。
我们在终端运行webpack后,会经历以下过程:
读取我们指定的配置文件(webpack.config.js)
从入口入口开始,分析我们的Module(模块),递归我们整个项目模块之间的依赖关系
加载对应的Loader,将这个Module(模块)解析成webpack可以识别的有效模块,并将它们添加到依赖图(Dependency graph)中
编译过程会触发多次风暴并执行配置好的Plugin(插件)
将剖析的模块分组为块
根据配置文件(输出),输出最终的Bundle
上述过程可以看作三个阶段:
可以概括为右图:
webpack实际的打包过程其实要复杂很多,这里为了更好理解,简化一下
去体验
让我们从实际出发,通过learn-02的案例,用实际代码来体验和理解webpack打包流程以及涉及到的概念。
我们看一下learn-02项目结构:
learn-02
├── index.html
├── package-lock.json
├── package.json
├── project.config.js
└── src
├── assets
│ └── style.less
├── index.js
├── plugin
│ ├── common.js
│ ├── index-vendors.js
│ └── share-vendors.js
└── share.js
我们来介绍一下对应的文件:
以下项目中的相关代码:
我们看到project.config.js的配置后,就可以猜测打包后会输出多少个文件了。
好啦,现在我们开始剖析:
1.当我们在终端运行 npm run build 时,webpack会读取我们指定的文件project.config.js
2、从入门开始,分析我们的模块。 我们的entry有两个index和share,所以此时会生成两个Chunk组:Chunk[index],Chunk[share],并且我们的模块对应的依赖会被递归。
3、index.js引入了style.less,所以它会加载对应的Loader,解析成webpack可以识别的有效模块,并添加到依赖图中。 此时会生成两个依赖图:
4.然后对该模块进行分组:
5、发现我们的配置中,commonjs也是通过代码分段的方式独立分离出来的,所以独立形成了一个chunk[initial-common]
6. 到目前为止,webpack 已经分离了三个块:
7.根据输出,最终输出Bundle
再次强调,实际的封装过程肯定要复杂得多
终于