chunk和bundle

Webpack从入口文件开始检索,并将具有依赖关系的模块生成一棵依赖树,最终得到一个chunk。由这个chunk得到的打包产物我们一般称之为bundle。

配置资源入口

webpack通过context和entry这两个配置项来共同决定入口文件的路径。在配置入口时,实际上做了两件事:

  1. 确定入口模块位置,告诉webpack从哪里开始打包
  2. 定义chunk name。如果工程只有一个入口,那么默认其chunk name为”main”;如果工程有多个入口,我们需要为每个入口定义chunk name,来作为该chunk的唯一标识。

context

context是用来为所有待打包资源文件设置基准地址的,设置了context值之后,对资源文件设置的相对路径就是相对于context值的,所以在配置时要求必须使用绝对路径的形式。

  1. const path = require("path");
  2. const HtmlWebpackPlugin = require('html-webpack-plugin');
  3. module.exports = {
  4. context:path.join(__dirname,'src'),
  5. entry:'./index.js',
  6. output:{
  7. path:path.join(__dirname,"dist"),
  8. filename:'build.js'
  9. },
  10. plugins:[
  11. new HtmlWebpackPlugin({
  12. title:'Development',
  13. template:'./index.html'
  14. })
  15. ],
  16. }

在webpack.config.js中,我们通过调用Node.js的路径拼装函数——path.join,将__dirname(Node.js内置全局变量,值为当前文件的绝对路径,在这里就是webpack.config.js的绝对路径)与其他目录拼装起来。

可以看到entry的值和插件HtmlWebpackPlugin的template值都是相对路径,而基准路径就是context。

entry

entry的配置有很多种形式:字符串、数组、对象和函数。可以根据需要来选择。

  1. 字符串类型。如上面代码所示。
  2. 数组类型入口

传入一个数组的作用是将多个资源预先合并,在打包时Webpack会将数组中的最后一个元素作为实际的入口路径。如:

  1. module.exports = {
  2. entry:['babel-polyfill','./src/index.js']
  3. }

上面的配置等同于:

  1. //webpack.config.js
  2. module.exports={
  3. entry:'./src/index.js',
  4. }
  5. //index.js
  6. import 'babel-polyfill';
  1. 对象类型入口

如果要定义多入口,则必须使用对象的形式。对象的属性名(key)就是chunk name,属性值(value)就是入口路径:

  1. module.exports = {
  2. entry:{
  3. index:['babel-polyfill','./src/index.js'],
  4. lib:'./src/lib.js',
  5. },
  6. }

属性值既可以是字符串,也可以是数组。

  1. 函数类型入口

只要该函数能够返回以上类型值即可。

  1. module.exports = {
  2. entry:()=>'./src/index.js',
  3. }

传入一个函数的优点在于我们可以在函数体内添加一些动态的逻辑来获取工程的入口。另外,函数也支持返回一个Promise对象进行异步操作。

  1. module.exports = {
  2. entry:()=> new Promise((resolve)=>{
  3. //模拟异步操作
  4. setTimeout(()=>{
  5. resolve('./src/index.js');
  6. },2000);
  7. }),
  8. };

实例

单页面应用(SPA)

指只有且只由一个入口进行对其他模块的引用。这样做的优点是只会生成一个js文件,且依赖关系清晰,但缺点是当应用规模上升到一定程度后导致产生的资源体积过大,降低用户的页面渲染速度。
Webpack默认认为一个bundle大于250kb(压缩前)就是过大了,会有【big】提示。

提取vendor

解决上述问题的方法就是提取Vendor,vendor一般指工程使用的库、框架等第三方模块(是被import到入口文件里的模块)集中打包而产生的bundle。

  1. module.exports = {
  2. entry:{
  3. index:'./src/index.js',
  4. vendor:['react','react-dom','react-router']
  5. }
  6. }

由于vendor仅仅包括第三方模块,这部分不会经常变动,因此可以有效的利用客户端缓存,在用户后续请求页面时会加快整体的渲染速度。

webpack4之后,提取vendor可以通过配置optimization.splitChunks实现

多页面应用

多个入口,每个bundle都对应一个页面,bundle加载的都是各个页面必要的逻辑。
另外,在多页面应用中,可以利用vendor对公共模块进行提取打包:

  1. module.exports = {
  2. entry:{
  3. pageA:'./src/pageA.js',
  4. pageB:'./src/pageB.js',
  5. pageC:'./src/pageC.js',
  6. vendor:['react','react-router']
  7. }
  8. }

配置资源出口

filename

对出口文件进行命名,但也不仅仅是一个bundle的名字,还可以是一个相对路径。

单入口

  1. module.exports = {
  2. entry:'./src/index.js',
  3. output:{
  4. filename:'bundle.js',
  5. }
  6. }

入口文件打包输出后就命名为bundle.js

多入口

  1. module.exports = {
  2. entry:{
  3. index:'./src/index.js',
  4. lib:'./src/lib.js'
  5. },
  6. output:{
  7. filename:'[name]@[chunkhash].js',
  8. }
  9. }

在多入口时,可以用模板变量进行命名

  • [name]:以chunk name命名bundle
  • [fullhash]:指代webpack此次打包所有资源生成的hash
  • [chunkhash]:指代当前chunk内容的hash
  • [id]:当前chunk 的id
  • [query]:filename配置项中的query

模板变量的作用:

  1. 区分bundle。不同的bundle,[name]、[chunkhash]、[id]都不同。
  2. 控制客户端缓存。[fullhash]和[chunkhash]都与chunk内容直接相关,在filename使用这些变量后,当chunk内容发生变化时,可以同时引起资源文件名的更改,从而使用户在下一次请求资源文件时会立即下载新的版本而不会使用本地缓存。[query]也可以起到类似的效果,但它与chunk内容无关,需要开发者手动指定。

path

指定资源输出的路径,必须是绝对路径,可以用path.join拼装。一定是资源打包后的实际位置。

publicPath

指定资源的请求位置。客户端请求资源的位置。
示例:

  1. entry: './src/index.js',
  2. output: {
  3. filename: '[name]@[chunkhash].js',
  4. path: path.join(__dirname, "dist"),
  5. publicPath:'/assets/'
  6. },

相关文献:
《webpack实战:入门、进阶与调优》居玉皓 著