1. 代码分割 #

2. 入口点分割 #

{
  entry: {
   page1: "./src/page1.js",
   page2: "./src/page2.js"
  }
}

https://static.zhufengpeixun.com/http_xie_yi_rfc2616_zhong_ying_wen_shuang_ban_1637749432228.zip

3 动态导入和懒加载 #

3.1 video.js #

hello.js

module.exports = "video";

index.js

document.querySelector('#play').addEventListener('click',() => {
    import('./video').then(result => {
        console.log(result.default);
    });
});

index.html

<button id="play">播放</button>

3.2 prefetch(预先拉取) #

<link rel="prefetch" href="utils.js" as="script">
button.addEventListener('click', () => {
  import(
    `./utils.js`
    /* webpackPrefetch: true */
    /* webpackChunkName: "utils" */
  ).then(result => {
    result.default.log('hello');
  })
});

3.3 preload(预先加载) #

$ npm install --save-dev webpackpreload-webpack-plugin

prefetchpreload

<link rel="preload" as="script" href="utils.js">
import(
  `./video.js`
  /* webpackPreload: true */
  /* webpackChunkName: "video" */
)

webpackpreload-webpack-plugin


const HtmlWebpackPlugin = require("html-webpack-plugin");
class WebpackpreloadWebpackPlugin {
  apply(compiler) {
    compiler.hooks.compilation.tap('PreloadWebpackPlugin', (compilation) => {
      HtmlWebpackPlugin.getHooks(compilation).alterAssetTags.tap('PreloadWebpackPlugin', (htmlData) => {
        const { publicPath, assetTags } = htmlData;
        const { entrypoints, moduleGraph, chunkGraph } = compilation;
        for (const entrypoint of entrypoints) {
          const preloaded = entrypoint[1].getChildrenByOrders(moduleGraph, chunkGraph).preload; // is ChunkGroup[] | undefined
          if (!preloaded) return;
          const chunks = new Set();
          for (const group of preloaded) {
            for (const chunk of group.chunks) chunks.add(chunk);
          }
          const files = new Set()
          for (const chunk of chunks) {
            for (const file of chunk.files) files.add(file);
          }
          const links = [];
          for (const file of files) {
            links.push({
              tagName: 'link',
              attributes: {
                rel: 'preload',
                href: `${publicPath}${file}`
              }
            });
          }
          assetTags.styles.unshift(...links);
        }
      });
    });
  }
}
module.exports = WebpackpreloadWebpackPlugin;

plugins\ImportPlugin.js

class ImportPlugin {
    apply(compiler) {
        compiler.hooks.compilation.tap(
            "ImportPlugin",
            (compilation, { normalModuleFactory }) => {
                normalModuleFactory.hooks.parser
                    .for("javascript/auto")
                    .tap("ImportPlugin", (parser) => {
                        parser.hooks.importCall.tap("ImportParserPlugin", expr => {
                            const { options } = parser.parseCommentOptions(expr.range);
                            console.log(options);
                        });
                    });
            }
        );
    }
}
module.exports = ImportPlugin;

3.4 preload vs prefetch #

4. 提取公共代码 #

4.1 为什么需要提取公共代码 #

4.2 如何提取 #

4.3 module chunk bundle #

4.4 splitChunks #

splitChunks

4.4.1 工作流程 #

4.4.1 webpack.config.js #

const HtmlWebpackPlugin = require('html-webpack-plugin');
const AssetPlugin = require('./asset-plugin');
module.exports = {
    mode: 'development',
    devtool: false,
    entry: {
        page1: "./src/page1.js",
        page2: "./src/page2.js",
        page3: "./src/page3.js",
    },
    optimization: {
        splitChunks: {
            // 表示选择哪些 chunks 进行分割,可选值有:async,initial和all
            chunks: 'all',
            // 表示新分离出的chunk必须大于等于minSize,默认为30000,约30kb。
            minSize: 0,//默认值是20000,生成的代码块的最小尺寸
            // 表示一个模块至少应被minChunks个chunk所包含才能分割。默认为1。
            minChunks: 1,
            // 表示按需加载文件时,并行请求的最大数目。默认为5。
            maxAsyncRequests: 3,
            // 表示加载入口文件时,并行请求的最大数目。默认为3
            maxInitialRequests: 5,
            // 表示拆分出的chunk的名称连接符。默认为~。如chunk~vendors.js
            automaticNameDelimiter: '~',
            cacheGroups: {
                defaultVendors: {
                    test: /[\\/]node_modules[\\/]/, //条件
                    priority: -10 ///优先级,一个chunk很可能满足多个缓存组,会被抽取到优先级高的缓存组中,为了能够让自定义缓存组有更高的优先级(默认0),默认缓存组的priority属性为负值.
                },
                default: {
                    minChunks: 2,////被多少模块共享,在分割之前模块的被引用次数
                    priority: -20
                },
            },
        },
        runtimeChunk: true
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html',
            chunks: ["page1"],
            filename: 'page1.html'
        }),
        new HtmlWebpackPlugin({
            template: './src/index.html',
            chunks: ["page2"],
            filename: 'page2.html'
        }),
        new HtmlWebpackPlugin({
            template: './src/index.html',
            chunks: ["page3"],
            filename: 'page3.html'
        }),
        new AssetPlugin()
    ]
}

4.4.2 webpack-assets-plugin.js #

plugins\webpack-assets-plugin.js

class WebpackAssetsPlugin {
  constructor(options) {
    this.options = options;
  }
  apply(compiler) {
    //每当webpack开启一次新的编译 ,就会创建一个新的compilation
    compiler.hooks.compilation.tap('WebpackAssetsPlugin', (compilation) => {
      //每次根据chunk创建一个新的文件后会触发一次chunkAsset
      compilation.hooks.chunkAsset.tap('WebpackAssetsPlugin', (chunk, filename) => {
        console.log(chunk.id, filename);
      });
    });
  }
}
module.exports = WebpackAssetsPlugin;

4.4.3 page1.js #

let module1 = require('./module1');
let module2 = require('./module2');
let $ = require('jquery');
console.log(module1,module2,$);
import( /* webpackChunkName: "asyncModule1" */ './asyncModule1');

4.4.4 page2.js #

let module1 = require('./module1');
let module2 = require('./module2');
let $ = require('jquery');
console.log(module1,module2,$);

4.4.5 page3.js #

let module1 = require('./module1');
let module3 = require('./module3');
let $ = require('jquery');
console.log(module1,module3,$);

4.4.6 module1.js #

module.exports = 'module1';

4.4.7 module2.js #

console.log("module2");

4.4.8 module3.js #

console.log("module3");

4.4.9 asyncModule1.js #

import _ from 'lodash';
console.log(_);

4.4.10 打包后的结果 #

//入口代码块
page1.js
page2.js
page3.js
//异步加载代码块
src_asyncModule1_js.js
//defaultVendors缓存组对应的代码块
defaultVendors-node_modules_jquery_dist_jquery_js.js
defaultVendors-node_modules_lodash_lodash_js.js
//default代缓存组对应的代码块
default-src_module1_js.js
default-src_module2_js.js

4.4.11 计算过程 #

let page1Chunk= {
    name:'page1',
    modules:['A','B','C','lodash']
}

let page2Chunk = {
    name:'page2',
    module:['C','D','E','lodash']
}

let  cacheGroups= {
    vendor: {
      test: /lodash/,
    },
    default: {
      minChunks: 2,
    }
};

let vendorChunk = {
    name:`vendor~node_modules_lodash_js`,
    modules:['lodash']
}
let defaultChunk = {
    name:`default~page1~page2`,
    modules:['C']
}

4.5 reuseExistingChunk #

4.5.1 index.js #


4.5.2 webpack.config.js #

const HtmlWebpackPlugin = require('html-webpack-plugin');
const AssetPlugin = require('./asset-plugin');
module.exports = {
    mode: 'development',
    devtool: false,
+   entry: './src/index.js',
    optimization: {
        splitChunks: {
            // 表示选择哪些 chunks 进行分割,可选值有:async,initial和all
            chunks: 'all',
            // 表示新分离出的chunk必须大于等于minSize,默认为30000,约30kb。
            minSize: 0,//默认值是20000,生成的代码块的最小尺寸
            // 表示一个模块至少应被minChunks个chunk所包含才能分割。默认为1。
            minChunks: 1,
            // 表示按需加载文件时,并行请求的最大数目。默认为5。
            maxAsyncRequests: 3,
            // 表示加载入口文件时,并行请求的最大数目。默认为3
            maxInitialRequests: 5,
            // 表示拆分出的chunk的名称连接符。默认为~。如chunk~vendors.js
            automaticNameDelimiter: '~',
+           cacheGroups: {
+               defaultVendors: false,
+               default: false,
+               common: {
+                   minChunks: 1,
+                   reuseExistingChunk: false
+               }
+           }
        },
+       runtimeChunk: false
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html',
            filename: 'index.html'
        })
        new AssetPlugin()
    ]
}

4.5.3 结果 #

//reuseExistingChunk: false
main main.js
common-src_index_js common-src_index_js.js

//reuseExistingChunk: true
main main.js