# Webpack 与浏览器缓存(Caching)

今天我们来讲一下 webpack 与浏览器缓存这一块内容。

想象一个场景,当我们的代码已经打包好的 dist 目录放到的服务器上,

当用户第一次访问 index.html 页面的时候,他会先下载我们所需的 vendors.chunk.jsmain.bundle.js 两个文件,然后在刷新一下页面,他其实不会再向向服务器请求上面两个 js 文件了,直接从浏览器的缓存中读取使用了。因为两个 js 文件的名字没有改变过。

假设我们的业务代码进行修改了,我们重新打包,发现打包出来的代码还是上图所示。这个时候我们再将代码部署到服务器上,用户访问页面的时候,发现还是这两个名字的 js,于是就又使用了浏览器缓存中的文件。

因此我们需要对打包出来的 js 文件做一个名字上的配置。

# 一个例子

我们修改入口文件 index.js

import _ from 'lodash';
import $ from 'jquery';

const dom = $('<div>');
dom.html(_.join(['Hello', 'darrell'], ' '));
$('body').append();

引入 lodashjquery 文件,在页面上插入 <div>Hello darrell</div> 元素。

我们修改线上配置文件 webpack.prod.jsoutput 配置,在输出的文件名字上加上相应的 [contenthash] 占位符,顾名思义,contenthash 就是根据文件内容生成的 hash 值,只有当文件内容发生改变的时候,才会发生改变:

...
const prodConfig = {
  ...
  output: {
    filename: "[name].[contenthash].js",
    chunkFilename: '[name].[contenthash].js', // 简介引入代码输出的名字
    path: path.resolve(__dirname, '../dist')
  }
  ...
}
...

我们打包一下 npm run build

我们会发现有一个警告,这个是 webpack 告诉我们我们打包出来的文件有点大,在性能上可能会有一些问题,这里我们可以暂时先不管,在 webpack.common.js 中加入:

...
module.exports = {
  ...
  performance: false, // 关闭性能上的一些问题
  ...
}
...

我们重新打包,可以看到打包出来的内容如下:

打包出来的内容中 main 开头的是我们的业务代码,然后 vendors 开头的就是我们的库代码,也就是 lodashjquery

接着我们修改一下 index.js 文件:

import _ from 'lodash';
import $ from 'jquery';

const dom = $('<div>');
- dom.html(_.join(['Hello', 'darrell'], ' '));
+ dom.html(_.join(['Hello', 'darrell123'], ' '));
$('body').append();

重新打包,可以看到打包出来 业务代码内容的 hash 变了,而库代码的内容 hash 没变。

这个时候用户重新访问我们的页面,js 的文件名字变了,这个时候便又会从服务器上面重新下载新的文件。而库对应的文件名字没有变,就还是会使用浏览器缓存中的代码。

# 老版本 Webpack

老版本的 Webpack 中的 contenthash 有的时候即使你的内容没有变化,他还是会改变 contenthash 的值,

原因是我们的业务代码和库代码之间有一些关联代码,在 webpack 中被称为 mainfest,默认情况下 mainfest 的代码会存在于 main.jsvendors.js 代码之中的,他在旧版的 webpack 中打包可能会有差异,所以才会造成我们的内容没有变化,但是 contenthash 的值变化了。

这个时候需要在公共配置文件 webpack.common.js 中增加一个配置:

...

module.exports = {
  ...
  optimization: {
    ...
    runtimeChunk: {
      name: 'runtime',
    },
    ...
  },
  ...
}
...

我们重新进行打包,会发现 dist 目录下多了 runtime 开头的一个文件:

上面的配置的意思就是将 manifest 关联代码抽离出来放到 runtime.js 中去,main.js 只写业务代码,vendors.js 只放库文件代码。

这个的时候在老版的 webpack 中也不会有相应的问题了。

# 相关链接

# 示例代码

示例代码可以看这里: