webpack笔记13-应用篇-缓存cache

337 阅读3分钟

前言

缓存可以降低网络流量,使网站加载速度更快。然而,如果我们在部署新版本时不更改资源的文件名,浏览器可能会认为它没有被更新,就会使用它的缓存版本。由于缓存的存在,当你需要获取新的代码时,就会显得很棘手。

我们可以通过必要的配置,确保 webpack 编译生成的文件能够被客户端缓存,而在文件内容变化后,能够请求到新的文件。

方式

1. 输出文件的文件名

通过使用 output.filename 进行文件名替换,可以确保浏览器获取到修改后的文件。[hash] 替换可以用于在文件名中包含一个构建相关(的 hash,但是更好的方式是使用 [chunkhash] 替换,在文件名中包含一个 chunk 相关的哈希

const HtmlWebpackPlugin=require('html-webpack-plugin') 
 module.exports = {
     entry: './src/main.js',
     devtool: 'inline-source-map',
     plugins:[
         new HtmlWebpackPlugin({
             title:'caching'
         })
     ],
     output:{
         filename:'[name].[chunkhash].js',//更改
     },
  };

打包结果如下。这个时候输出时,资源名都会被动态修改,这样就不会因为缓存而取不到新的文件了。

image.png

如果我们不做修改,再次运行构建,我们以为文件名会保持不变。然而,如果我们真的运行,可能会发现文件名可能会变,也可能不会。这也是因为 webpack 在入口 chunk 中,包含了某些样板(boilerplate,webpack 运行时的引导代码),特别是 runtime 和 manifest

2. 提取模板

基于上面的问题,我们需要将 webpack 的样板和 manifest 提取出来。官方文档写的是用CommonsChunkPlugin方式,但我这边报错了,所以选择使用optimization.splitChunks的方式提取

image.png

const HtmlWebpackPlugin=require('html-webpack-plugin') 
 module.exports = {
     //...
     //提取manifest
     optimization:{
        runtimeChunk:{
            name:'manifest'
        },
        splitChunks: {
            chunks:"async"
        },
     },
     //...
  };

打包后可见被提取出来了

image-20200821130830590.png


optimization.splitChunks也可以将第三方库(library)提取到单独的 vendor chunk 文件中,因为它们很少像本地的源代码那样频繁修改。因此通过提取,利用客户端的长效缓存机制,可以通过命中缓存来消除请求,并减少向服务器获取资源,同时还能保证客户端代码和服务器端代码版本一致:

假设这里安装了第三方库router

const HtmlWebpackPlugin=require('html-webpack-plugin') 
 module.exports = {
     entry: {
         main:'./src/main.js',
         vender:['router'] //如果要提取多个库,可以在数组里添加,这里指提取router
     },
     //...
     optimization:{
        //...
        splitChunks: {
            chunks:"async",
            //设置
            cacheGroups:{
                vender:{
                    name:"vender",//指定chunk的名字
                    chunks:'initial',
                }
            }
        },
     },
     //...
  };

可以发现第三方库被成功提取出来了 image.png

3.模块标识符

这时候,当我们添加一个模块print.js,并引入main.js

//print.js
export default function print(text) {
    console.log(text)
}

//main.js
import{print}from'./print' //引入
function component(){
    var el=document.createElement('div');
    el.innerHTML="123"
    console.log(print('222')) 
    return el
}
document.body.appendChild(component())

发现其中main bundle的hash会随着自身内容的新增而变化,而vender bundle我们并没有修改,然而hash也变化了:

image.png

HashedModuleIdsPlugin可以解决这个问题:

const webpack = require('webpack');
 module.exports = {
     //...
     plugins:[
     //...
         new webpack.HashedModuleIdsPlugin()
     ],
     //...
  };

这个时候当我们移除依赖print.js会发现只有main.js发生变化:

9b614033b92b6ab42638463c5612ea4.png

存疑:官方文档说:manifest bundle会因为当前包含一个新模块的引用而发生变化。然而这里的hash并没有发生变化,不知道是提取的问题还是什么问题。