一文搞懂webpack hash持久化的原理

目录
  • 理解 module、chunk 和 bundle
  • hash 的分类
    • hash
    • runtime 和 manifest
  • chunkhash
  • contentHash
  • webpack5
  • 如何使用 hash 做缓存呢?
    • 静态资源服务器的缓存
    • max-age 配合 hash 做使用
  • 参考文章

理解 module、chunk 和 bundle

  • module 就是我们通过 import 引入的各种模块
  • chunk 是 webpack 根据功能拆分出来的模块,包括入口文件, 动态 import,lazy 等的文件以及 splitChunks 拆分出来的代码,chunk 可能包含多个 module
  • bundle 就是 webpack 打包之后的各个文件,于 chunk 一般一一对应

hash 的分类

  • hash:the hash of the module identifier(根据 module_id 序列的变化而变化)
  • chunkHash:the hash of the chunk content(chunkHash,根据每一个 chunk 内容的变化而变化)
  • contentHash:the hash of extracted content(根据内容变化而变化)

hash

  • compilation

    • webpack 的 hash 是根据 compilation 计算出来的,compilation 对象代表某个版本的资源对应的编译进程,当我们的文件发生改变的时候, 进而能够针对改动生产全新的编译文件。compilation 对象包含当前模块资源、待编译文件、有改动的文件和监听依赖的所有信息,如果我们修改某一个文件,那么此时整个项目的 hash 都会改变
  • compiler
    • compiler 对象代表的是配置完备的 Webpack 环境。 compiler 对象只在 Webpack 启动时构建一次,由 Webpack 组合所有的配置项构建生成,compiler 对象代表的是不变的 webpack 环境,compilation 是针对随时可变的项目文件
  • module_id
    • webpack 通过给每一个模块一个 module_id 来处理各个模块之间的依赖关系,而默认的 id 命名规则是根据模块引入的顺序赋予一个整数(1,2,3),所以任意的增添或者删除一个模块的依赖,都会对整个的 ID 序列产生影响,最后影响 hash 值,这些模块会被 runtime 和 manifest 和引用到
  • 对于图片、字体、PDF 等资源该 hash 还是可以生成一个唯一值的

    • 此时我们配置 webpack 的 output 为 hash

      //  此时项目的
      mode: 'production',
      entry: {
          app: [path.resolve(__dirname, '../src/index.js')],
      },
      output: {
          filename: 'js/[name].[hash].js',
          hashDigestLength: 7,
          path: path.resolve(__dirname, '../dist'),
          publicPath: './',
      },
      
    • 项目依赖打包情况如下,我们可以看到所有的 hash 的值都是一样的
                      Asset       Size  Chunks                                Chunk Names
                css/app.2f3933e.css   52 bytes       0  [emitted] [immutable]         app
               css/list.2f3933e.css    1.5 KiB       1  [emitted] [immutable]         list
            css/vendors.2f3933e.css   71.2 KiB       2  [emitted] [immutable]         vendors
         css/vendors.2f3933e.css.gz   7.85 KiB          [emitted]
                         index.html   1.33 KiB          [emitted]
                  js/app.2f3933e.js   6.63 KiB       0  [emitted] [immutable]         app
                 js/list.2f3933e.js   50.9 KiB       1  [emitted] [immutable]         list
         js/list.2f3933e.js.LICENSE   120 bytes         [emitted]
              js/list.2f3933e.js.gz     15 KiB          [emitted]
              js/vendors.2f3933e.js    340 KiB       2  [emitted] [immutable]  [big]  vendors
      js/vendors.2f3933e.js.LICENSE  423 bytes          [emitted]
           js/vendors.2f3933e.js.gz   91.8 KiB          [emitted]
                 js/work.2f3933e.js  188 bytes       3  [emitted] [immutable]         work
      

runtime 和 manifest

  • webpack 通过 runtime 和 manifest 来管理所有模块的交互
  • runtime
    • runtime,以及伴随的 manifest 数据,主要是指:在浏览器运行过程中,webpack 用来连接模块化应用程序所需的所有代码。它包含:在模块交互时,连接模块所需的加载和解析逻辑。包括:已经加载到浏览器中的连接模块逻辑,以及尚未加载模块的延迟加载逻辑
  • manifest
    • 当 compiler 开始执行、解析和映射应用程序时,它会保留所有模块的详细要点。这个数据集合称为 "manifest",当完成打包并发送到浏览器时,runtime 会通过 manifest 来解析和加载模块。无论你选择哪种 模块语法,那些 import 或 require 语句现在都已经转换为 webpack_require 方法,此方法指向模块标识符(module identifier)。通过使用 manifest 中的数据,runtime 将能够检索这些标识符,找出每个标识符背后对应的模块
  • runtime 和 manifest 是一个每次打包都可能变化的不稳定的因素,所以他会导致一些问题,比如,我们对整个项目的文章在做一次打包,打包结果如下,我们发现,我们什么也没有改动但是 hash 全部发生了变化,原因就是 runtime 和 manifest 这些所谓的样板文件

                    Asset       Size  Chunks                                Chunk Names
                 css/app.2f3933e.css   52 bytes       0  [emitted] [immutable]         app
                css/list.2f3933e.css    1.5 KiB       1  [emitted] [immutable]         list
             css/vendors.2f3933e.css   71.2 KiB       2  [emitted] [immutable]         vendors
          css/vendors.2f3933e.css.gz   7.85 KiB          [emitted]
                          index.html   1.33 KiB          [emitted]
                   js/app.2f3933e.js   6.63 KiB       0  [emitted] [immutable]         app
                  js/list.2f3933e.js   50.9 KiB       1  [emitted] [immutable]         list
          js/list.2f3933e.js.LICENSE  120 bytes          [emitted]
                js/list.2f3933e.js.gz     15 KiB          [emitted]
                js/vendors.2f3933e.js    340 KiB       2  [emitted] [immutable]  [big]  vendors
        js/vendors.2f3933e.js.LICENSE  423 bytes          [emitted]
              js/vendors.2f3933e.js.gz   91.8 KiB          [emitted]
                    js/work.2f3933e.js  188 bytes       3  [emitted] [immutable]         work
    
  • 如何解决这个问题

    • 我们可以把 runtime 和 manifest 提取出来,去掉这两个不稳定因素,然后打包发现 hash 并未改变,但是我们多了一个 mainfest 文件
    optimization: {
        runtimeChunk: {
            name: 'manifest',
        },
    }
    
    • 再次打包代码,不断的打包 hash 都不会改变

                  Asset       Size  Chunks                                Chunk Names
                css/app.c870f3f.css        52 bytes        0  [emitted] [immutable]         app
               css/list.c870f3f.css        1.5 KiB         1  [emitted] [immutable]         list
            css/vendors.c870f3f.css        71.2 KiB        3  [emitted] [immutable]         vendors
         css/vendors.c870f3f.css.gz        7.85 KiB           [emitted] index.html 1.4 KiB [emitted]
                  js/app.c870f3f.js        3.62 KiB        0  [emitted][immutable] app
                 js/list.c870f3f.js        50.9 KiB        1  [emitted][immutable] list
         js/list.c870f3f.js.LICENSE        120 bytes          [emitted]
              js/list.c870f3f.js.gz        15 KiB             [emitted]
             js/manifest.c870f3f.js        3.07 KiB         2 [emitted][immutable] manifest
              js/vendors.c870f3f.js        340 KiB          3 [emitted][immutable] [big] vendors
      js/vendors.c870f3f.js.LICENSE       423 bytes          [emitted]
           js/vendors.c870f3f.js.gz       91.8 KiB           [emitted]
                 js/work.c870f3f.js       188 bytes        4 [emitted][immutable] work
      

chunkhash

  • chunk 就是模块。chunkhash 也就是根据模块内容计算出的 hash 值,很显然,hash 并不适合做本地持久化,所以我们使用 chunkhash 此时修改 webpack 的配置

    ```javascript
    optimization: webpackBase.optimization,
    mode: 'production',
    entry: {
        app: [path.resolve(__dirname, '../src/index.js')],
    }
    ```
    
  • 修改配置之后打包的结果是(CSS 的结果还是一样的,我们稍后处理)
                Asset       Size  Chunks                                Chunk Names
                css/app.8b9de76.css   71.3 KiB       0  [emitted] [immutable]         app
             css/app.8b9de76.css.gz   7.88 KiB          [emitted]
             css/vendors.8b9de76.css    1.5 KiB       3  [emitted] [immutable]         vendors
                          index.html   1.27 KiB          [emitted]
                   js/app.0df5dd7.js    340 KiB       0  [emitted] [immutable]  [big]  app
           js/app.0df5dd7.js.LICENSE  423 bytes          [emitted]
                 js/app.0df5dd7.js.gz   92.5 KiB          [emitted]
                   js/list.111956e.js   2.48 KiB       1  [emitted] [immutable]         list
               js/manifest.aa8eb6d.js   3.13 KiB       2  [emitted] [immutable]         manifest
                js/vendors.49e3e7f.js   48.5 KiB       3  [emitted] [immutable]         vendors
        js/vendors.49e3e7f.js.LICENSE  120 bytes          [emitted]
             js/vendors.49e3e7f.js.gz   13.8 KiB          [emitted]
                   js/work.1b2fd82.js  188 bytes       4  [emitted] [immutable]         work
    
  • 这个时候修改 list.js,然后继续打包,css 的 hash 变了,正常因为它使用的是 hash 不是 chunkhash,list 的 hash 也变了,正常因为我们修改了这个文件,work 的 hash 并没有变化,完全正常
    ```javascript
            Asset       Size  Chunks                                Chunk Names
                     css/app.1a93a35.css   71.3 KiB       0  [emitted] [immutable]         app
                  css/app.1a93a35.css.gz   7.88 KiB          [emitted]
                  css/vendors.1a93a35.css    1.5 KiB       3  [emitted] [immutable]         vendors
                               index.html   1.27 KiB          [emitted]
                        js/app.0df5dd7.js    340 KiB       0 [emitted] [immutable]  [big]  app
                js/app.0df5dd7.js.LICENSE  423 bytes         [emitted]
                     js/app.0df5dd7.js.gz   92.5 KiB         [emitted]
                       js/list.5b187e3.js   2.48 KiB       1 [emitted] [immutable]         list
                   js/manifest.3752b77.js   3.13 KiB       2 [emitted] [immutable]         manifest
                    js/vendors.49e3e7f.js   48.5 KiB       3 [emitted] [immutable]         vendors
            js/vendors.49e3e7f.js.LICENSE   120 bytes        [emitted]
                 js/vendors.49e3e7f.js.gz   13.8 KiB         [emitted]
                       js/work.1b2fd82.js   188 bytes      4 [emitted] [immutable] work
    ```
    
  • 这个时候我们为 list.js 引入一个新的 js,css 改变我们暂且不论,这个时候发现 vendors.js, app.js, work.js 竟然全部改变了, 这不符合我们的预期,这是因为每个 module.id 会基于默认的解析顺序(resolve order)进行增量(类似于没有指定 key 的 react 的组件的渲染)。也就是说,当解析顺序发生变化,ID 也会随之改变,所以我们需要自己命名这个 moduleid
    ```javascript
            Asset       Size  Chunks                                Chunk Names
                 css/app.636f1cd.css   52 bytes       0  [emitted] [immutable]         app
                css/list.636f1cd.css    1.5 KiB       1  [emitted] [immutable]         list
             css/vendors.636f1cd.css   71.2 KiB       3  [emitted] [immutable]         vendors
          css/vendors.636f1cd.css.gz   7.85 KiB          [emitted]
                          index.html    1.4 KiB          [emitted]
                   js/app.0b4c163.js   3.62 KiB       0  [emitted][immutable] app
                  js/list.d02fa6a.js     51 KiB       1  [emitted][immutable] list
          js/list.d02fa6a.js.LICENSE  120 bytes          [emitted]
                js/list.d02fa6a.js.gz     15 KiB          [emitted]
               js/manifest.3a9ff17.js   3.09 KiB       2  [emitted][immutable]
       manifest js/vendors.a1bfd17.js    340 KiB       3  [emitted][immutable] [big] vendors
        js/vendors.a1bfd17.js.LICENSE  423 bytes          [emitted]
             js/vendors.a1bfd17.js.gz   91.8 KiB          [emitted]
                   js/work.f70d2d8.js  188 bytes         4 [emitted][immutable] work
    ```
    
  • 我们自己命名这个 ID 把,命名的方式如下

    // 将默认的数字 id 命名规则换成路径的方式。webpack 4 中当 mode 为 development 会默认启动
    optimization: {
        namedModules: true
    }
    // 但是如果把路径作为ID难免太长,所以我们使用HashedModuleIdsPlugin来生成hash
    plugins: [
        new webpack.HashedModuleIdsPlugin(),
    ],
    
    // 此时进行打包的结果是
    
        Asset       Size  Chunks                                Chunk Names
                  css/app.d36a7df.css   52 bytes       0  [emitted] [immutable]         app
                 css/list.d36a7df.css    1.5 KiB       1  [emitted] [immutable]         list
              css/vendors.d36a7df.css   71.2 KiB       3  [emitted] [immutable]         vendors
           css/vendors.d36a7df.css.gz   7.85 KiB          [emitted]
                           index.html    1.4 KiB          [emitted]
                    js/app.5aef12b.js   3.74 KiB      0  [emitted] [immutable]         app
                   js/list.9dbf1d7.js   51.5 KiB      1  [emitted] [immutable]         list
           js/list.9dbf1d7.js.LICENSE  120 bytes         [emitted]
                js/list.9dbf1d7.js.gz   15.7 KiB         [emitted]
               js/manifest.cf2b1ee.js   3.09 KiB      2  [emitted] [immutable]         manifest
                js/vendors.612571f.js    343 KiB      3  [emitted] [immutable]  [big]  vendors
        js/vendors.612571f.js.LICENSE  423 bytes         [emitted]
             js/vendors.612571f.js.gz   97.7 KiB         [emitted]
                   js/work.3d8d43d.js  196 bytes      4  [emitted] [immutable]         work
    
  • 此时为 list 再次 import 一个文件,打包之后 hash 的值是,此时我们发现 app.js 的值没有变,list 的值改变了,vendors 和 work 都没变完全符合我们的预期,至此 js hash 的过程已经完全结束
    ```javascript
        Asset       Size  Chunks                                Chunk Names
                css/app.39db041.css   52 bytes       0  [emitted] [immutable]         app
                css/list.39db041.css    1.5 KiB       1  [emitted] [immutable]         list
             css/vendors.39db041.css   71.2 KiB       3  [emitted] [immutable]         vendors
          css/vendors.39db041.css.gz   7.85 KiB          [emitted]
                          index.html    1.4 KiB          [emitted]
                   js/app.5aef12b.js   3.74 KiB       0  [emitted] [immutable]         app
                  js/list.a0c9911.js   51.5 KiB       1  [emitted] [immutable]         list
          js/list.a0c9911.js.LICENSE  120 bytes          [emitted]
                js/list.a0c9911.js.gz   15.7 KiB          [emitted]
               js/manifest.14406a1.js   3.09 KiB       2  [emitted] [immutable]         manifest
                js/vendors.612571f.js    343 KiB       3  [emitted] [immutable]  [big]  vendors
        js/vendors.612571f.js.LICENSE  423 bytes          [emitted]
             js/vendors.612571f.js.gz   97.7 KiB          [emitted]
                    js/work.3d8d43d.js  196 bytes       4  [emitted] [immutable]         work
    ```
    

contentHash

  • 之前我们还有一个遗留问题,就是 css 的 hash 每次都会产生变化,是因为我们之前配置了抽离的 css 是 hash,根据上面的文章,我们修改为 chunkhash

    ```javascript
    // 之前的配置
    miniCssExtract: new MiniCssExtractPlugin({
        filename: 'css/[name].[hash].css',
        chunkFilename: 'css/[name].[hash].css',
        ignoreOrder: false,
    });
    // 修改之后的配置
    miniCssExtract: new MiniCssExtractPlugin({
        filename: 'css/[name].[chunkhash].css',
        chunkFilename: 'css/[name].[chunkhash].css',
        ignoreOrder: false,
    });
    ```
    
  • 修改为 chunkhash 之后,当然 css 的值就不会每次都发生变化了,此时我们对项目进行打包,然后修改 work.js 我们会发现 css 的 hash 并没有发生(此处不在尝试) 任何变化,完全符合我们的预期,但是我们却发现,我们是以 chunk 做 hash,所以导致了一个问题,list.js 和 list.css 的 hash 值一摸一样,因为他们属于同一个 chunk
    ```javascript
        Asset       Size  Chunks                                Chunk Names
                 css/app.5aef12b.css   52 bytes       0  [emitted] [immutable]         app
                css/list.a0c9911.css    1.5 KiB       1  [emitted] [immutable]         list
             css/vendors.612571f.css   71.2 KiB       3  [emitted] [immutable]         vendors
          css/vendors.612571f.css.gz   7.85 KiB          [emitted]
                          index.html    1.4 KiB          [emitted]
                   js/app.5aef12b.js   3.74 KiB       0  [emitted] [immutable]         app
                  js/list.a0c9911.js   51.5 KiB       1  [emitted] [immutable]         list
          js/list.a0c9911.js.LICENSE  120 bytes          [emitted]
                js/list.a0c9911.js.gz   15.7 KiB          [emitted]
               js/manifest.171619f.js   3.12 KiB       2  [emitted] [immutable]         manifest
                js/vendors.612571f.js    343 KiB       3  [emitted] [immutable]  [big]  vendors
        js/vendors.612571f.js.LICENSE  423 bytes          [emitted]
              js/vendors.612571f.js.gz   97.7 KiB          [emitted]
                    js/work.3d8d43d.js  196 bytes       4  [emitted] [immutable]         work
    ```
    
  • 对 app.css 做修改,然后重新打包,打包结果如下,我们发现,app.css 的 hash 发生了变化,但是 app.js 的 hash 也发生了变化,这就是因为 app.css 和 app.js 属于同一个 chunk,所以这个时候我们就必须对 css 单独处理让他根据自己的 content 去做 hash 而不是 chunk

    ```javascript
        Asset       Size  Chunks                                Chunk Names
                  css/app.131454e.css   52 bytes       0  [emitted] [immutable]         app
                 css/list.a0c9911.css    1.5 KiB       1  [emitted] [immutable]         list
              css/vendors.612571f.css   71.2 KiB       3  [emitted] [immutable]         vendors
           css/vendors.612571f.css.gz   7.85 KiB          [emitted]
                           index.html    1.4 KiB          [emitted]
                    js/app.131454e.js   3.74 KiB       0  [emitted] [immutable]         app
                   js/list.a0c9911.js   51.5 KiB       1  [emitted] [immutable]         list
           js/list.a0c9911.js.LICENSE  120 bytes          [emitted]
                js/list.a0c9911.js.gz   15.7 KiB          [emitted]
               js/manifest.171619f.js   3.12 KiB       2  [emitted] [immutable]         manifest
                js/vendors.612571f.js    343 KiB       3  [emitted] [immutable]  [big]  vendors
        js/vendors.612571f.js.LICENSE  423 bytes          [emitted]
             js/vendors.612571f.js.gz   97.7 KiB          [emitted]
                   js/work.3d8d43d.js  196 bytes       4  [emitted] [immutable]         work
    ```
    
  • 修改配置然后重新打包代码
    // 修改配置
    miniCssExtract: new MiniCssExtractPlugin({
        filename: 'css/[name].[contenthash].css',
        chunkFilename: 'css/[name].[contenthash].css',
        ignoreOrder: false,
    });
    
    /* 重新打包代码如下,可以看到app.js和app.css的hash不一致了 */
        Asset       Size  Chunks                                Chunk Names
                  css/app.15e0de3.css   52 bytes       0  [emitted] [immutable]         app
                 css/list.8298c67.css    1.5 KiB       1  [emitted] [immutable]         list
              css/vendors.353f491.css   71.2 KiB       3  [emitted] [immutable]         vendors
           css/vendors.353f491.css.gz   7.85 KiB          [emitted]
                           index.html    1.4 KiB          [emitted]
                    js/app.131454e.js   3.74 KiB       0  [emitted] [immutable]         app
                   js/list.a0c9911.js   51.5 KiB       1  [emitted] [immutable]         list
           js/list.a0c9911.js.LICENSE  120 bytes          [emitted]
                js/list.a0c9911.js.gz   15.7 KiB          [emitted]
               js/manifest.88160aa.js   3.12 KiB       2  [emitted] [immutable]         manifest
                js/vendors.612571f.js    343 KiB       3  [emitted] [immutable]  [big]  vendors
        js/vendors.612571f.js.LICENSE  423 bytes          [emitted]
             js/vendors.612571f.js.gz   97.7 KiB          [emitted]
                   js/work.3d8d43d.js  196 bytes       4  [emitted] [immutable]         work
    
  • 修改 app.css,然后再次打包代码,打包结果如下,我们发现 除了 app.css hash 改变,app.js 的 hash 一样的发生了改变,这又是为什么呢,通过试验是因为 CSS moduley 引起的问题,因为 css 文件的改变也会改变到 js,初步猜测是 css module 的问题,经过试验发现即使去掉 cssMOdule 还是有同样的问题
    ```javascript
                                    Asset       Size  Chunks                                Chunk Names
             css/app.29ae3c7.css   52 bytes       0  [emitted] [immutable]         app
             css/list.8298c67.css    1.5 KiB       1  [emitted] [immutable]         list
          css/vendors.353f491.css   71.2 KiB       3  [emitted] [immutable]         vendors
       css/vendors.353f491.css.gz   7.85 KiB          [emitted]
                       index.html    1.4 KiB          [emitted]
                js/app.3bafc2a.js   3.74 KiB       0  [emitted] [immutable]         app
               js/list.a0c9911.js   51.5 KiB       1  [emitted] [immutable]         list
       js/list.a0c9911.js.LICENSE  120 bytes          [emitted]
            js/list.a0c9911.js.gz   15.7 KiB          [emitted]
           js/manifest.88160aa.js   3.12 KiB       2  [emitted] [immutable]         manifest
            js/vendors.612571f.js    343 KiB       3  [emitted] [immutable]  [big]  vendors
    js/vendors.612571f.js.LICENSE  423 bytes          [emitted]
         js/vendors.612571f.js.gz   97.7 KiB          [emitted]
               js/work.3d8d43d.js  196 bytes       4  [emitted] [immutable]         work
    ```
    
  • 后来一想,其实跟上面的 app.js 和 app.css hash 一样是同样的问题,app.js 的改变,就是会改变 chunk 的值,所以把修改 webpack 的配置如下
            Asset       Size  Chunks                                Chunk Names
                css/app.44b7866.css   38 bytes       0  [emitted] [immutable]         app
               css/list.8298c67.css    1.5 KiB       1  [emitted] [immutable]         list
            css/vendors.353f491.css   71.2 KiB       3  [emitted] [immutable]         vendors
          css/vendors.353f491.css.gz   7.85 KiB          [emitted]
                          index.html    1.4 KiB          [emitted]
                   js/app.ca738c5.js   3.67 KiB       0  [emitted] [immutable]         app
                  js/list.1895c1a.js   51.5 KiB       1  [emitted] [immutable]         list
          js/list.1895c1a.js.LICENSE  120 bytes          [emitted]
               js/list.1895c1a.js.gz   15.7 KiB          [emitted]
               js/manifest.b7ee988.js   3.12 KiB       2  [emitted] [immutable]         manifest
                js/vendors.38fec86.js    343 KiB       3  [emitted] [immutable]  [big]  vendors
        js/vendors.38fec86.js.LICENSE  423 bytes          [emitted]
             js/vendors.38fec86.js.gz   97.7 KiB          [emitted]
                   js/work.ea3817c.js  196 bytes       4  [emitted] [immutable]         work
    
    /* 修改css之后然后再次打包,果然解决了之前的问题 */
    
        Asset       Size  Chunks                                Chunk Names
                 css/app.208b221.css   39 bytes       0  [emitted] [immutable]         app
                css/list.8298c67.css    1.5 KiB       1  [emitted] [immutable]         list
             css/vendors.353f491.css   71.2 KiB       3  [emitted] [immutable]         vendors
           css/vendors.353f491.css.gz   7.85 KiB          [emitted]
                           index.html    1.4 KiB          [emitted]
                    js/app.ca738c5.js   3.67 KiB       0  [emitted] [immutable]         app
                   js/list.1895c1a.js   51.5 KiB       1  [emitted] [immutable]         list
           js/list.1895c1a.js.LICENSE  120 bytes          [emitted]
                js/list.1895c1a.js.gz   15.7 KiB          [emitted]
               js/manifest.b7ee988.js   3.12 KiB       2  [emitted] [immutable]         manifest
                js/vendors.38fec86.js    343 KiB       3  [emitted] [immutable]  [big]  vendors
        js/vendors.38fec86.js.LICENSE  423 bytes          [emitted]
             js/vendors.38fec86.js.gz   97.7 KiB          [emitted]
                   js/work.ea3817c.js  196 bytes       4  [emitted] [immutable]         work
    
        /* 移除一个list引用的模块,再次打包,完全符合预期 */
                Asset       Size  Chunks                                Chunk Names
                  css/app.208b221.css   39 bytes       0  [emitted] [immutable]         app
                 css/list.8298c67.css    1.5 KiB       1  [emitted] [immutable]         list
              css/vendors.353f491.css   71.2 KiB       3  [emitted] [immutable]         vendors
           css/vendors.353f491.css.gz   7.85 KiB          [emitted]
                           index.html    1.4 KiB          [emitted]
                    js/app.ca738c5.js   3.67 KiB       0  [emitted] [immutable]         app
                   js/list.271a546.js   51.5 KiB       1  [emitted] [immutable]         list
           js/list.271a546.js.LICENSE  120 bytes          [emitted]
                js/list.271a546.js.gz   15.7 KiB          [emitted]
               js/manifest.a2e6ed1.js   3.12 KiB       2  [emitted] [immutable]         manifest
                js/vendors.38fec86.js    343 KiB       3  [emitted] [immutable]  [big]  vendors
        js/vendors.38fec86.js.LICENSE  423 bytes          [emitted]
             js/vendors.38fec86.js.gz   97.7 KiB          [emitted]
                   js/work.ea3817c.js  196 bytes       4  [emitted] [immutable]         work
    

webpack5

webpack5 对moduleIds & chunkIds的优化,不在是以数字作为id

optimization:{
    moduleIds:'deterministic',
    chunkIds:'deterministic'
},

如何使用 hash 做缓存呢?

很多人知道 hash,但是要不项目配置为 hash,不利于做长期缓存,要不前端配置好了,但是不知道如何配合后端做长期缓存,这就涉及到 http 缓存的

Etag - Last-Modified

  • 1、客户端请求一个页面 A
  • 2、服务器返回页面 A,并在给 A 加上一个 Last-Modified(Mon, 22 Mar 2018 10:10:10 GMT)和 ETag(2e681a-6-5d044840)
  • 3、客户端展现该页面,并将页面连同 Last-Modified/ETag 一起缓存
  • 4、客户再次请求页面 A,并将上次请求时服务器返回的 Last-Modified/ETag 一起传递给服务器,也就是说发送 If-None-Match 头,这个头的内容 就是 2e681a-6-5d044840,发送 If-Modified-Since(Mon, 22 Mar 2018 10:10:10 GMT)
  • 5、服务器判断发送过来的 Etag 和 Last-Modified 与本地匹配,如果没有修改,不返回 200,返回 304,直接返回响应 304 和一个空的响应体,当然响应头也会包含 Last-Modified(Mon, 22 Mar 2018 10:10:10 GMT)和 ETag(2e681a-6-5d044840)

Cache-control

  • Cache-control 判断浏览器是否需要发送请求而不需要服务器对比,常见的取值有 private、no-cache、max-age、must- revalidate、no-store 等,默认为 private,Cache-control 值为“no-cache”时,访问此页面不会在 Internet 临时文章夹留下页面备份
  • 打开新窗口
    • 值为 private、no-cache、must-revalidate,那么打开新窗口访问时都会重新访问服务器。 而如果指定了 max-age 值,那么在此值内的时间里就不会重新访问服务器,例如: Cache-control: max-age=5(表示当访问此网页后的 5 秒 内再次访问不会去服务器)
  • 在地址栏回车
    • 值为 private 或 must-revalidate 则只有第一次访问时会访问服务器,以后就不再访问。 值为 no-cache,那么每次都会访问。 值为 max-age,则在过期之前不会重复访问
  • 按后退按扭
    • 值为 private、must-revalidate、max-age,则不会重访问, 值为 no-cache,则每次都重复访问
  • 按刷新按扭或者 F6
    • 无论为何值,都会重复访问

Expires

  • Expires 和 max-age 都可以用来指定文档的过期时间,但是也有不同
  • Expires 指定一个绝对的过期时间(GMT 格式)
  • max-age 指定的是从文档被访问后的存活时间,这个时间是个相对值(比如:3600s),相对的是文档第一次被请求时服务器记录的 Request_time(请求时间)
  • 有的服务器, max-age 是这样计算出来的,expires - request_time

静态资源服务器的缓存

  • 如果是第一次访问,请求报文首部不会包含相关字段,服务端在发送文件前做如下处理

    • 如服务器支持 ETag,设置 ETag 头
    • 如服务器支持 Last-Modified,设置 Last-Modified 头
    • 设置 Expires 头 + 设置 Cache-Control 头(设置其 max-age 值)浏览器收到响应后会存下这些标记,并在下次请求时带上与 ETag 对应的请求首部 If-None-Match 或与 Last-Modified 对应的请求首部 If-Modified-Since
  • 如果是重复的请求
    • 浏览器判断缓存是否过期(通过 Cache-Control 和 Expires 确定, 两者都存在 Cache-Control为主)

      • 如果未过期,直接使用缓存内容,也就是强缓存命中,并不会产生新的请求
      • 如果已过期,会发起新的请求,并且请求会带上 If-None-Match 或 If-Modified-Since,或者兼具两者(两者都存在Etag 为主)
      • 服务器收到请求,进行缓存的新鲜度再验证:
        • 首先检查请求是否有 If-None-Match 首部,没有则继续下一步,有则将其值与文档的最新 ETag 匹配,失败则认为缓存不新鲜,成功则继续下一步
        • 接着检查请求是否有 If-Modified-Since 首部,没有则保留上一步验证结果,有则将其值与文档最新修改时间比较验证,失败则认为缓存不新鲜,成功则认为缓存新鲜
        • 当两个首部皆不存在或者验证结果是不新鲜时,发送 200 及最新文件,并在首部更新新鲜度。
        • 当验证结果是缓存仍然新鲜时(也就是弱缓存命中),不需发送文件,仅发送 304,并在首部更新新鲜度

max-age 配合 hash 做使用

在保持 hash 不变性的前提下,我们可以使用 max-age 来设置前端缓存

/* 具体设置多少,个人觉得要看升级的频率,在保证hash不变性的前提下,设置1y 比较合理https://expressjs.com/zh-cn/guide/using-middleware.html */
app.use(
  express.static(path.join(__dirname, "public"), {
    maxAge: "1y",
    expires: "1y",
    Etag: false,
    lastModified: false,
  })
);

参考文章

到此这篇关于一文搞懂webpack hash持久化的原理的文章就介绍到这了,更多相关webpack hash持久化内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解webpack中的hash、chunkhash、contenthash区别

    hash.chunkhash.contenthash hash一般是结合CDN缓存来使用,通过webpack构建之后,生成对应文件名自动带上对应的MD5值.如果文件内容改变的话,那么对应文件哈希值也会改变,对应的HTML引用的URL地址也会改变,触发CDN服务器从源服务器上拉取对应数据,进而更新本地缓存.但是在实际使用的时候,这几种hash计算还是有一定区别. 我们先建一个测试案例来模拟下: 项目结构 我们的项目结构很简单,入口文件index.js,引用了index.css.然后新建了jquer

  • 一文搞懂webpack hash持久化的原理

    目录 理解 module.chunk 和 bundle hash 的分类 hash runtime 和 manifest chunkhash contentHash webpack5 如何使用 hash 做缓存呢? 静态资源服务器的缓存 max-age 配合 hash 做使用 参考文章 理解 module.chunk 和 bundle module 就是我们通过 import 引入的各种模块 chunk 是 webpack 根据功能拆分出来的模块,包括入口文件, 动态 import,lazy 等

  • 一文搞懂Java MD5算法的原理及实现

    目录 MD5加密简介 MD5加密原理 MD5加密常用方法 MD5加密简介 哈希算法又称散列算法,是将任何数据转换成固定长度的算法的统称. 从本质上讲,MD5也是一种哈希算法,其输出是生成128位的输出结果. 如果输入两个不同的明文,就会输出两个不同的输出值,并且根据输出值,不能得到原始的明文,这个过程是不可逆的. MD5加密原理 MD5算法对512位报文的输入信息进行处理,每个报文被分成16个32位报文. 经过一系列处理后,算法的输出由4个32位的数据包组成,这些数据包级联生成一个128位的哈希

  • 一文搞懂Spring循环依赖的原理

    目录 简介 循环依赖实例 测试 简介 说明 本文用实例来介绍@Autowired解决循环依赖的原理.@Autowired是通过三级缓存来解决循环依赖的. 除了@Autoired,还有其他方案来解决循环依赖的,见:Spring循环依赖的解决方案详解 概述 @Autowired进行属性注入可以解决循环依赖.原理是:Spring控制了bean的生命周期,先实例化bean,后注入bean的属性.Spring中记录了正在创建中的bean(已经实例化但还没初始化完毕的bean),所以可以在注入属性时,从记录

  • 一文搞懂MySQL持久化和回滚的原理

    目录 redo log 为什么要先更新内存数据,不直接更新磁盘数据? 为什么需要redo log? redo log是如何实现的? 为什么一个block设计成512字节? 为什么要两段式提交? crash后是如何恢复的? undo log 什么情况下会生成undo log? undo log是如何回滚的? undo log存在什么地方? redo log 事务的支持是数据库区分文件系统的重要特征之一,事务的四大特性: 原子性:所有的操作要么都做,要么都不做,不可分割. 一致性:数据库从一种状态变

  • 一文搞懂阿里云服务器部署Redis并整合Spring Boot

    目录 一.什么是Redis 二.Redis的优缺点 三.阿里云服务器部署安装Redis ️在Linux服务器新建文件夹存放Redis 测试连接 四.关闭防火墙,配置Redis访问端口 配置Redis 关闭防火墙 阿里云控制台添加6379接口访问 五.Spring Boot 整合 Redis ️项目结构 核心源码 ️测试结果 总结 一.什么是Redis redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链

  • 一文搞懂JAVA 修饰符

    Java语言提供了很多修饰符,主要分为以下两类: 访问修饰符 非访问修饰符 修饰符用来定义类.方法或者变量,通常放在语句的最前端.我们通过下面的例子来说明: public class ClassName { // ... } private boolean myFlag; static final double weeks = 9.5; protected static final int BOXWIDTH = 42; public static void main(String[] argum

  • 一文搞懂Spring中的注解与反射

    目录 前言 一.内置(常用)注解 1.1@Overrode 1.2@RequestMapping 1.3@RequestBody 1.4@GetMapping 1.5@PathVariable 1.6@RequestParam 1.7@ComponentScan 1.8@Component 1.9@Service 1.10@Repository 二.元注解 @Target @Retention @Documented @Inherited 三.自定义注解 四.反射机制概述 4.1动态语言与静态语

  • 一文搞懂Java中的序列化与反序列化

    目录 序列化和反序列化的概念 应用场景 序列化实现的方式 继承Serializable接口,普通序列化 继承Externalizable接口,强制自定义序列化 serialVersionUID的作用 静态变量不会被序列化 使用序列化实现深拷贝 常见序列化协议对比 小结 序列化和反序列化的概念 当我们在Java中创建对象的时候,对象会一直存在,直到程序终止时.但有时候可能存在一种"持久化"场景:我们需要让对象能够在程序不运行的情况下,仍能存在并保存其信息.当程序再次运行时 还可以通过该对

  • 一文搞懂ES6中的Map和Set

    Map Map对象保存键值对.任何值(对象或者原始值) 都可以作为一个键或一个值.构造函数Map可以接受一个数组作为参数. Map和Object的区别 •一个Object 的键只能是字符串或者 Symbols,但一个Map 的键可以是任意值. •Map中的键值是有序的(FIFO 原则),而添加到对象中的键则不是. •Map的键值对个数可以从 size 属性获取,而 Object 的键值对个数只能手动计算. •Object 都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突.

  • 一文搞懂hashCode()和equals()方法的原理

    Java中的超类java.lang.Object 有两个非常重要的方法: public boolean equals(Object obj) public int hashCode() 这两个方法最开发者来说是十分重要的,必须清楚的理解,但实际上,甚至很多经验丰富的Java开发者有时候也没有真正搞清楚这两个方法的使用和原理.当我们自定义了对象,并且想要将自定义的对象加到Map中时,我们就必须对自定义的对象重写这两个方法,才能正确使用Map.我们接下来将用这篇文章指出在使用hashcode和equ

随机推荐