在前两节的基础和高级配置中,我们学习了怎么样去配置Webpack实现不同目的的构建打包,其中多次提及了entry
,loader
,plugin
,module
,chunk
,bundle
等这样的名词,那么它们都是些什么?有什么区别和联系?
module,chunk和bundle
在官网里是这样介绍Webpack的:
我们在官网首页也可以看到这么一个图片去阐述Webpack的作用:
我们可以通过这张图去理解module
,chunk
和bundle
的区别和联系:
- 其中左边部分可以理解为
module
,就是各个源码文件,webpack的世界中,一切皆模块,只要可以被引用的都是模块。 - 中间部分就是
chunk
,多模块合并成的,如:entry
,import()
,splitChunk
等都会产生chunk
,做依赖分析 。 - 右边部分就是
bundle
,即最终输出的文件。
Webpack的结构
先来看一下Webpack配置文件输出的配置对象的完整结构:
|-- webpack.config.js
|-- entry # 定义构建依赖图的入口
|-- output # 定义输出bundle的名字和位置
|-- module # 定义loader
|-- plugins # 定义插件
|-- mode # 选择开发 or 生成模式
|-- devServer # 开发环境启动服务
|-- optimization # 手动配置优化项
entry(入口)
__入口起点(entry point)__指示 webpack 应该使用哪个模块,来作为构建其内部 依赖图(dependency graph) 的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。
在前两节介绍了entry
的两种配置方式:
单入口
用法: entry: string | [string]
module.exports = {
entry: './path/to/my/entry/file.js'
};
多入口
用法:entry: { <entryChunkName> string | [string] } | {}
每一对键值对都是一个单独入口,是独立分离的依赖图,因此会产生不同的chunk
。
module.exports = {
entry: {
app: './src/app.js',
adminApp: './src/adminApp.js'
}
};
为什么需要多入口?
如果我们的程序是多页面程序,那么就需要在Webpack配置多入口。在多页面应用程序中,server 会拉取一个新的 HTML 文档给客户端。页面重新加载此新文档,并且资源被重新下载。然而,使用 optimization.splitChunks 为页面间共享的应用程序代码创建 bundle。由于入口起点数量的增多,多页应用能够复用多个入口起点之间的大量代码/模块,从而可以极大地从减少重复代码的数量,减少最终打包后的体积。
output(输出)
output
属性告诉 webpack 在哪里输出它所创建的 bundle
,以及如何命名这些文件。
注意,即使可以存在多个 entry 起点,但只能指定一个 output
配置。
单入口
module.exports = {
output: {
filename: 'bundle.js',
}
};
此配置将一个单独的 bundle.js
文件输出到 dist
目录中。
多入口
如果配置中创建出多于一个 chunk
,则应该使用 占位符来确保每个文件具有唯一的名称。
module.exports = {
entry: {
app: './src/app.js',
search: './src/search.js'
},
output: {
filename: '[name].js',
path: __dirname + '/dist'
}
};
loader
webpack 只能理解 JavaScript
和 JSON
文件。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中。
在 webpack 的配置中,loader 有两个属性:
test
:识别出哪些文件会被转换。use
:定义出在进行转换时,应该使用哪个 loader。
module.exports = {
module: {
rules: [
{ test: /\.css$/, use: 'css-loader' },
{ test: /\.ts$/, use: 'ts-loader' }
]
}
};
plugin(插件)
loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。 插件目的在于解决 loader 无法实现的其他事。 想要使用一个插件,只需要 require() 它,然后把它添加到 plugins 数组中。多数插件可以通过选项(option)自定义。也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new 操作符来创建一个插件实例。
onst HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装
const webpack = require('webpack'); // 用于访问内置插件
module.exports = {
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
]
},
plugins: [
new HtmlWebpackPlugin({template: './src/index.html'})
]
};
原理浅析
webpack 插件是一个具有 apply
方法的 JavaScript
对象。apply
方法会被 webpack compiler
调用,并且在整个编译生命周期都可以访问 compiler
对象。
ConsoleLogOnBuildWebpackPlugin.js
:
const pluginName = 'ConsoleLogOnBuildWebpackPlugin';
class ConsoleLogOnBuildWebpackPlugin {
apply(compiler) {
compiler.hooks.run.tap(pluginName, compilation => {
console.log('webpack 构建过程开始!');
});
}
}
module.exports = ConsoleLogOnBuildWebpackPlugin;
mode(模式)
通过选择 development
, production
或 none
之中的一个,来设置 mode
参数,就会启用 webpack 内置在相应环境下的优化。其默认值为 production
。
用法:string = 'production': 'none' | 'development' | 'production'
模式 | 功能 | development | 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 development . 为模块和 chunk 启用有效的名。 | production | 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 production 。为模块和 chunk 启用确定性的混淆名称,FlagDependencyUsagePlugin ,FlagIncludedChunksPlugin ,ModuleConcatenationPlugin ,NoEmitOnErrorsPlugin 和 TerserPlugin 。 | none | 不使用任何默认优化选项 |
---|
optimization(优化)
从 webpack 4 开始,会根据你选择的mode
来执行不同的优化,不过所有的优化还是可以手动配置和重写。
下面介绍一下我们前两节的配置使用到的optimization
相关插件。
minimizer(压缩)
通过提供一个或多个定制过的 TerserPlugin
实例, 覆盖默认压缩工具(minimizer
),将产出代码进行压缩。
用法:[TerserPlugin]
或 [function (compiler)]
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
optimization: {
minimizer: [
// 压缩JS
new TerserPlugin({
cache: true,
parallel: true,
sourceMap: true, // 如果在生产环境中使用 source-maps,必须设置为 true
terserOptions: {
// https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions
}
}),
// 压缩CSS
new OptimizeCSSAssetsPlugin({}),
],
}
};
splitChunks(分割代码块)
在Webpack 4以前,chunks
都是通过依赖图进行连接的,用CommonsChunkPlugin
避免重复的依赖的chunk
,但是想要更进一步的优化是没有其他任何办法的。
Webpack 4以后,CommonsChunkPlugin
删除了,而改为optimization.splitChunks
,来自定义代码块的分割,实现更精细的代码分割和复用,更好地完成按需加载。
默认值
webpack将根据以下条件自动分割代码块chunk
:
- 可以共享新块,或者模块来自node_modules文件夹
- 新的块 >= 20kb(在min + gz之前)
- 当按需加载块时,并行请求的最大数量 <= 30
- 初始页面加载时并行请求的最大数量 <= 30
自定义
我们来回顾一下前两节optimization.splitChunks
的配置:
chunks
:选择哪些块进行优化
取值:string = 'all' | 'async' | 'initial'
'async'
: 异步 chunk,只对异步导入的文件处理'initial'
: 入口 chunk,对于异步导入的文件不处理'all'
: 在异步和非异步块之间也可以共享块
cacheGroups
:缓存分组
cacheGroups
结构是一个对象,每个属性就对应于一个chunk
的配置,key就是这个chunk
的名字,可配置的项有:
name
:chunk
的名字priority
:一个模块可以属于多个缓存组,优化将优先选择具有较高的缓存组priority
,数值越大,优先级越高test
:控制此缓存组选择的模块,省略它会选择所有模块。它可以匹配绝对模块资源路径或块名称。匹配块名称时,将选择块中的所有模块minSize
:模块的大小限制minChunks
:模块最少复用的次数,只有大于等于这个值才会被抽取出来
module.exports = {
optimization: {
// 分割代码块
splitChunks: {
chunks: 'all',
// 缓存分组
cacheGroups: {
// 第三方模块
vendor: {
name: 'vendor', // chunk 名称
priority: 1, // 权限更高,优先抽离
test: /node_modules/,
minSize: 0, // 大小限制
minChunks: 1 // 最少复用过几次
},
// 公共的模块
common: {
name: 'common', // chunk 名称
priority: 0, // 优先级
minSize: 0, // 公共模块的大小限制
minChunks: 2 // 公共模块最少复用过几次
}
}
}
}
};
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!