美文网首页
Webpack如何抽取公共代码

Webpack如何抽取公共代码

作者: VivaLaVida_692c | 来源:发表于2020-01-10 15:29 被阅读0次

    如果要彻底明白 Webpack如何抽取公共代码,就要设计一个场景来支持抽取公共代码的多种形式,能够从代码运行的结果中查看实际的效果,从效果中深入理解各个参数的作用。

    场景设计

    在设计场景之前,首先要明白公共代码抽取常见的几种情况:

    抽取 Webpack 运行时代码

    直接加载的代码抽取(静态引入)

    node_modules 中的代码

    项目中的业务代码

    按需加载的代码抽取(动态引入)

    node_modules 中的代码

    项目中的业务代码

    经过分析会发现,现在常见的场景就五种,设计一个应用场景包含这五种情况,就可以很好的理解 Webpack如何抽取公共代码了。

    设计场景如下:

    其中带有 ~ 的表示是动态引入

    module 是一个独立的功能模块,chunk 是很多 module 打包的结果,bundle 是很多 chunk 最后生成的结果

    打包入口动态和静态导入的模块:

    入口模块A模块B模块C模块D

    pageAmoduleAmoduleBmoudleCmoudleD~

    pageBmoduleAmoduleBmoudleC~moudleD~

    pageCmoduleAmoduleB~moudleC~moudleD~

    pageDmoduleA~moduleB~moudleC~moudleD~

    模块中动态和静态导入的 node_modules 中的模块:

    模块reactvuelodashjquery

    moduleAreactvuelodashjquery~

    moduleBreactvuelodash~jquery~

    moduleCreactvue~lodash~jquery~

    moduleDreact~vue~lodash~jquery~

    入口文件中的代码(以 pageA.js 为例,其他的入口文件中的代码类似):

    import './module/moduleA';

    import './module/moduleB';

    import './module/moduleC';

    import('./module/moduleD');

    console.log('pageA');

    模块中的代码(以 moduleA.js 为例,其他的模块文件中的代码类似):

    import 'react';

    import 'vue';

    import 'lodash';

    import('jquery');

    console.log('moduleA');

    export default 'moduleA';

    最终打包之后的预期效果:

    node_modules 的按需加载模块在一个 chunk 中,包含 react、vue、lodash、jquery

    node_modules 的直接加载的模块在一个 chunk 中,包含 react、vue、lodash

    项目中按需加载的模块在一个 chunk 中,包含 moduleA、moduleB、moduleC、moduleD

    项目中直接加载的模块在一个 chunk 中,包含 moduleA、moduleB、moduleC

    有一个 Webpack 的运行时 chunk:runtime.bundle.js

    import(‘react‘);

    import(‘vue‘);

    import(‘lodash‘);

    import(‘jquery‘);

    认识配置

    在 Webpack 中已经提供了抽取公共代码的默认配置。

    官方给出的默认配置:

    module.exports = {

      optimization: {

        splitChunks: {

          automaticNameDelimiter: '~',

          name: true,

          chunks: 'async',

          minSize: 30000,

          minChunks: 1,

          maxAsyncRequests: 5,

          maxInitialRequests: 3,

          cacheGroups: {

            vendors: {

              test: /[\\/]node_modules[\\/]/,

              priority: -10

            },

            default: {

              minChunks: 2,

              priority: -20,

              reuseExistingChunk: true

            }

          }

        }

      }

    };

    各个选项的说明:

    optimization.automaticNameDelimiter:指定生成名字中的分隔符,Webpack 将使用 chunk 的名字和 chunk 的来源,如 vendors~main.js

    optimization.name:分割块的名称,提供 true 会自动生成基于 chunk 和缓存组键的名称

    optimization.maxAsyncRequests:按需加载时,并行请求的最大数量,默认是 5

    optimization.maxInitialRequests:一个入口最大的并行请求数,默认是 3

    optimization.minChunks:在分割之前,这个代码块最小应该被引用的次数,默认是 1

    optimization.chunks:需要关心的属性,一般可以指定三种形式 all(全部的 chunk,包含所有类型的 chunk)、async(按需加载的 chunk) 和 initial(初始的 chunk)

    optimization.minSize:需要关心的属性,一个新的 chunk 的最小体积,默认是 30000,即 30K

    optimization.cacheGroups:该属性是一个对象,上面的属性可以在该对象中使用,如果在该对象中不使用,则默认继承上面的属性值

    cacheGroups:是一个对象,下面的所有属性都是自定义的,一般是打包后的模块名称

    cacheGroups.name:需要关心的属性,抽取公共代码的 chunk 名字

    cacheGroups.priority:需要关心的属性,抽取公共代码的优先级,数字越大,优先级越高

    cacheGroups.reuseExistingChunk:需要关心的属性,是否重用 chunk,如果当前块包含已经从主bundle中分离出来的模块,那么它将被重用,而不是生成一个新的模块,一般设置为 true

    cacheGroups.test:需要关心的属性,匹配规则,一般使用正则表达式来匹配,如 /[\\/]node_modules[\\/]/ 是匹配 node_modules中的模块

    下面的例子中主要是演示上面指出的需要关心的属性。

    准备工作

    为了能够更好的查看程序执行的效果,需要做以下几个准备工作。

    1.创建 package.json 并安装相关依赖和指定运行的 scripts

      "scripts": {

        "build": "webpack --mode production"

      },

      "devDependencies": {

        "webpack": "^4.17.1",

        "webpack-cli": "^3.1.0"

      },

      "dependencies": {

        "jquery": "^3.3.1",

        "lodash": "^4.17.10",

        "react": "^16.4.2",

        "vue": "^2.5.17"

      }

    2.创建 Webpack 的配置文件 webpack.config.js,并输入公共的配置内容

    const { resolve } = require('path');

    const webpackConfig = {};

    // 入口

    webpackConfig.entry = {

      pageA: './src/pageA.js',

      pageB: './src/pageB.js',

      pageC: './src/pageC.js',

      pageD: './src/pageD.js'

    };

    // 出口

    webpackConfig.output = {

      path: resolve(__dirname, './dist'),

      filename: '[name].bundle.js',

      // chunkFilename 的作用就是设置 chunk 的名字,抽取公共代码的时候有用

      chunkFilename: "[id].[name].chunk.js"

    };

    // 优化相关

    webpackConfig.optimization = {

      splitChunks: {

        automaticNameDelimiter: '~',

        name: true,

        maxAsyncRequests: 5,

        maxInitialRequests: 3,

        minChunks: 1,

        minSize: 0, // 这里自定义不管文件有多小,都要抽取公共代码

        cacheGroups: {}

      }

    };

    module.exports = webpackConfig;

    3.执行命令

    $ yarn build

    查看未抽取公共代码的效果

    上面准备工作中配置的 webpack.config.js 是没有经过抽取公共代码的,执行命令之后,会发现控制台中输出下面的结果:

              Asset      Size                  Chunks            Chunk Names

      6.6.chunk.js  284 bytes                        6  [emitted]

      0.0.chunk.js  219 bytes                        0  [emitted]

      2.2.chunk.js  7.21 KiB                        2  [emitted]

      3.3.chunk.js  69.3 KiB                        3  [emitted]

      4.4.chunk.js  64.3 KiB                        4  [emitted]

      5.5.chunk.js  85.3 KiB                        5  [emitted]

      1.1.chunk.js  311 bytes                        1  [emitted]

      7.7.chunk.js  217 bytes                        7  [emitted]

    pageA.bundle.js    143 KiB  8, 0, 2, 3, 4, 6, 7, 12  [emitted]  pageA

    pageB.bundle.js    143 KiB    9, 0, 2, 3, 4, 7, 12  [emitted]  pageB

    pageC.bundle.js    143 KiB      10, 0, 2, 3, 4, 12  [emitted]  pageC

    pageD.bundle.js  2.22 KiB                      11  [emitted]  pageD

    12.12.chunk.js  190 bytes                      12  [emitted]

    从上面的结果中可以看出,没有抽取公共代码,下面就逐步优化,来抽取公共代码。

    抽取 Webpack 运行时代码

    在抽取 Webpack 运行时代码的时候,需要指定 runtimeChunk 属性:

    true:表示每个入口都抽取一个公共的运行时代码

    ‘single‘:表示多个入口抽取一个公共的运行时代码,一般使用这种方式

    // 抽取运行时代码

    webpackConfig.optimization.runtimeChunk = 'single';

    执行命令之后,查看控制台:

                Asset      Size                  Chunks            Chunk Names

        7.7.chunk.js  284 bytes                        7  [emitted]

        0.0.chunk.js  219 bytes                        0  [emitted]

        2.2.chunk.js  7.21 KiB                        2  [emitted]

        3.3.chunk.js  69.3 KiB                        3  [emitted]

        4.4.chunk.js  64.3 KiB                        4  [emitted]

        5.5.chunk.js  85.3 KiB                        5  [emitted]

    runtime.bundle.js  2.16 KiB                        6  [emitted]  runtime

        1.1.chunk.js  311 bytes                        1  [emitted]

        8.8.chunk.js  217 bytes                        8  [emitted]

    9.pageA.chunk.js    141 KiB  9, 0, 2, 3, 4, 7, 8, 13  [emitted]  pageA

    10.pageB.chunk.js    141 KiB    10, 0, 2, 3, 4, 8, 13  [emitted]  pageB

    11.pageC.chunk.js    141 KiB      11, 0, 2, 3, 4, 13  [emitted]  pageC

    12.pageD.chunk.js  328 bytes                      12  [emitted]  pageD

      13.13.chunk.js  190 bytes                      13  [emitted]

    这时,会发现多了一个 runtime.bundle.js 文件,这个文件就是 Webpack 的运行时代码。

    抽取公共代码

    下面抽取的公共代码就只包含了四部分:项目中的静态导入、项目中的动态导入、node_modules 中的静态导入、node_modules 中的动态导入。

    这四种情况,自己划分了一下优先级,可以在代码中看出来。

    node_modules 中的直接加载的代码:

    webpackConfig.optimization.splitChunks.cacheGroups.nodeSrc = {

      name: 'nodeSrc',

      reuseExistingChunk: true,

      test: /[\\/]node_modules[\\/]/,

      chunks: 'initial', // 指定为初始的 chunk

      priority: 3

    };

    node_modules 中的按需加载的代码:

    webpackConfig.optimization.splitChunks.cacheGroups.nodeAsync = {

      name: 'nodeAsync',

      reuseExistingChunk: true,

      test: /[\\/]node_modules[\\/]/,

      chunks: 'async', // 指定为按需加载的 chunk

      priority: 2

    };

    项目中的直接加载的代码:

    webpackConfig.optimization.splitChunks.cacheGroups.commonSrc = {

      name: 'commonSrc',

      reuseExistingChunk: true,

      chunks: 'initial', // 指定为初始的 chunk

      priority: 1

    };

    项目中的按需加载的代码:

    webpackConfig.optimization.splitChunks.cacheGroups.commonAsync = {

      name: 'commonAsync',

      reuseExistingChunk: true,

      chunks: 'async', // 指定为按需加载的 chunk

      priority: 0

    };

    执行命令之后,可以看到打包之后的最终效果:

                    Asset      Size  Chunks            Chunk Names

    0.commonAsync.chunk.js  946 bytes      0  [emitted]  commonAsync

      1.nodeAsync.chunk.js    226 KiB    1, 4  [emitted]  nodeAsync

      2.commonSrc.chunk.js  1.22 KiB      2  [emitted]  commonSrc

        runtime.bundle.js  2.19 KiB      3  [emitted]  runtime

        4.nodeSrc.chunk.js    141 KiB      4  [emitted]  nodeSrc

          5.pageA.chunk.js  74 bytes      5  [emitted]  pageA

          6.pageB.chunk.js  74 bytes      6  [emitted]  pageB

          7.pageC.chunk.js  74 bytes      7  [emitted]  pageC

          8.pageD.chunk.js  72 bytes      8  [emitted]  pageD

    如果感觉这种带数字的名字查看不直观,就修改 output 中的 chunkFilename 的值为 "[name].chunk.js"。

    最终经过 Webpack 打包后的代码就是预期的结果。

    相关文章

      网友评论

          本文标题:Webpack如何抽取公共代码

          本文链接:https://www.haomeiwen.com/subject/nmnhactx.html