目錄:
  1. 前言
    1. Webpack 3 和 Webpack 4 实现 Code Splitting 的区别
      1. 通过Webpack 3 实现 Code Splitting
        1. 通过Webpack 4 实现 Code Splitting
          1. 题目叙述
      2. 总结

        力扣笔试题-Webpack的代码切割和长效化缓存(3版和4版)

        閱讀時間:全文 1054 字,預估用時 6 分鐘
        創作日期:2020-04-28
        文章標籤:
         
        BEGIN

        前言

        leetcode有道题叫用 Webpack 实现 predictable long term cache, 点击访问 🔗

        predictable long term cache中文翻译就是可预测的长效缓存, 说白了就是代码切割(Code Splitting), 通过代码切割的方式细化打包规则, 并用文件文本hash值标记文件, 当文件修改重新打包时会修改hash值, 否则文件内容与文件名不变, 以达到浏览器长期缓存的效果.

        Webpack 3 和 Webpack 4 实现 Code Splitting 的区别

        Webpack 3 是通过在plugins下添加插件webpack.optimize.CommonsChunkPluginentry的配合实现

        Webpack 4 经过改版后将Code Splitting直接通过配置原生API实现, 对应APIoptimization.splitChunks

        对比Webpack 3 和 Webpack 4 的实现方式, Webpack 4 更明确更简单也更好配置, 可以看看他们的具体实现

        通过Webpack 3 实现 Code Splitting

        我们看下一个简单的Webpack 3 的示例, 只留下entry 和 plugins

        {
          entry: {
            chunks-name: ['npm package']
          },
          plugins: [
            new webpack.optimize.CommonsChunkPlugin(config)
          ]
        }
        

        假设我们要将echarts模块单独生成一个echarts文件, 将reactreact-domreact-redux等react相关包生成react文件, 其它从/node_modules/出来的模块打包成common文件, app文件是js入口文件应排除react文件、echarts文件和common文件代码, 我们可以这样配置:

        {
          // entry 里的每一项都是一个可打包文件
          entry: {
            // app是js主文件, 也是js的入口文件
            app: [require.resolve('./polyfills'), paths.appIndexJs],
            // echarts模块单独打包成echarts文件
            echarts: ['echarts'],
            // react、react-dom、react-redux模块打包成react文件
            react: ['react', 'react-dom', 'react-redux']
          },
          plugins: [
            // 将entry里的文件以最小化关联形式打包
            new webpack.optimize.CommonsChunkPlugin({
              name: ['echarts', 'react'],
              minChunks: Infinity,
            }),
            // 从./node_modules文件夹里取的其它模块打包到common文件
            new webpack.optimize.CommonsChunkPlugin({
              name: 'common',
              minChunks: module =>
                module.resource
                && /\.js$/.test(module.resource)
                && module.resource.indexOf(path.join(process.cwd(), './node_modules')) === 0,
              chunks: ['app'],
            }),
            // manifest文件起到对js文件的映射关系
            new webpack.optimize.CommonsChunkPlugin({
              name: 'manifest',
              chunks: ['react', 'common', 'echarts', 'app'],
            })
          ]
        }

        通过Webpack 4 实现 Code Splitting

        Webpack 4 映入内置的splitChunks API配置起来会简单很多, 以leetcode笔试题为例:

        题目叙述

        文件目录 🔗如下:

        .
        ├── package.json
        ├── src
        │   ├── common
        │   │   └── index.js
        │   ├── index.js
        │   ├── module-1
        │   │   └── index.js
        │   └── module-2
        │       └── index.js
        ├── webpack.config.js

        配置Webpack后打包输出如下:

                           Asset       Size  Chunks             Chunk Names
         common~main.438f8dd8.js  201 bytes       0  [emitted]  common~main
                main.9d46ca0f.js  170 bytes       1  [emitted]  main
        module1~main.01269684.js  212 bytes       2  [emitted]  module1~main
        module2~main.177226e7.js  214 bytes       3  [emitted]  module2~main
        runtime~main.04a66190.js   1.46 KiB       4  [emitted]  runtime~main
        vendors~main.62d56e50.js   69.7 KiB       5  [emitted]  vendors~main

        修改common/index.js, 将三个!!!去掉两个重新打包, common-main文件的hash值了, 其它文件hash值没变

        根据题意我们可以直接使用optimization.splitChunks.cacheGroups配置, 配置代码如下:

        {
          optimization: {
            // 通过runtimeChunk API自动生成runtime文件
            runtimeChunk: true,
            splitChunks: {
              // 设置最大生成的请求文件数, 默认为3, 这个必须设置, 要不然就只会生成3个文件
              maxInitialRequests: 10,
              // cacheGroups是splitChunks配置的关键, 很灵活
              cacheGroups: {
                // 生成vendors~main文件
                vendors: {
                  test: /[\\/]node_modules[\\/]/,
                  name: 'vendors~main',
                  chunks: 'all'
                },
                // 生成common~main文件
                commons: {
                  name: 'common~main',
                  test: /src\/common/,
                  chunks: 'initial',
                  priority: 10,
                  minSize: 0,
                },
                // 生成module1~main和module2~main文件
                module: {
                  name: module => module.context.split('/').pop().replace('-', '') + '~main',
                  test: /src\/module-/,
                  chunks: 'initial',
                  priority: 10,
                  minSize: 0,
                },
              },
            },
          },
        }

        Webpack 的代码比较含蓄API众多, 中文翻译总是会慢两拍, 刚开始博主没有设置maxInitialRequests值总是打包不出对应的文件数量, 最后通过Webpack的源码跟踪才找到的答案, 我们可以看看关键代码:

        通过代码跟踪我们发现node_modules/webpack/lib/SplitChunksPlugin.js的703行是关键代码, 它通过filter函数过滤掉, 使我们的打包工作不顺利, 摘录代码:

        validChunks = validChunks.filter(chunk => {
          // respect max requests when not enforced
          const maxRequests = chunk.isOnlyInitial()
            ? item.cacheGroup.maxInitialRequests
            : chunk.canBeInitial()
            ? Math.min(
                item.cacheGroup.maxInitialRequests,
                item.cacheGroup.maxAsyncRequests
              )
            : item.cacheGroup.maxAsyncRequests;
          return (
            !isFinite(maxRequests) || getRequests(chunk) < maxRequests
          );
        });

        当我们在cacheGroups的每一项里没有传maxInitialRequests, 则默认区的optimization.splitChunks.maxInitialRequests, 当没设置maxInitialRequests时maxRequests = item.cacheGroup.maxInitialRequests =mization.splitChunks.maxInitialRequests = 3, 因此getRequests(chunk) < maxRequests总是能返回false来控制chunk的数量

        总结

        通过Webpack 3 和 Webpack 4 对code spliting的配置实现predictable long term cache, Webpack 4 通过直接修改splitChunks API也更简单明了, 但由于splitChunks API里增加了很多配置参数不易理解, 可读性和易用性个人感觉不如Webpack 3.

        FINISH

        隨機文章
        人生倒計時
        default