我们使用 webpack 来打包我们的项目时,将产生一个可部署的/dist 目录。一旦将/dist 的内容部署到服务器上,浏览器将访问该服务器获取资源信息。因此,浏览器会使用缓存策略,这使得网站加载速度更快,不必要的网络流量更少。但是,当您需要获取新代码时,它也许会带来麻烦。
Long-term caching
Long-term caching 策略是这样的:给静态文件一个很长的缓存过期时间,比如一年。然后在给文件名里加上一个 hash,每次构建时,当文件内容改变时,文件名中的 hash 也会改变。浏览器在根据文件名作为文件的标识,所以当 hash 改变时,浏览器就会重新加载这个文件。下面我们来看个例子:
基础配置:
// webpack.config.js
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: {
main: './src/app',
},
output: {
path: path.join(__dirname, 'build'),
filename: '[name].[hash:8].js'
}
};
- 以上代码中输出的filename配置为全局hash,例如项目中常见的:对 app 模块的任何更改同时也会导致我们的 vendors 模块的 hash 失效,此时我们可以将文件名中把
hash改成chunkhash(它只会使用它自己的模块中的内容来生成哈希值),那么更改app 模块中的内容, vendor 模块不会再受影响了。 - 但如果我们在app.js中引入a.css文件,那么 app 模块的hash值将发生改变,原因很简单,因为它们属于同一个chunk,因此,我们需要使用
contenthash,这样当a.css文件更新时,app 模块的hash值就不会发生改变了。
关于 hash 、chunkhash 、contenthash作用域的理解:
-
hash 是针对整个项目的,这个项目就是被hash的对象,这个项目内任何文件发生变化,都会导致整个项目的hash值发生改变.
-
chunkhash 是根据webpack配置文件的entry,从入口文件开始,有依赖的文件发生变动,那么输出文件的chunkhash都会发生改变.
-
contenthash 就仅仅只是针对当前文件的内容.
但当我们增加新的依赖时,新的问题又来了(例如在app.js文件中引入a.js文件):
// app.js
import a from './a';
尽管我们的 vendors 模块没有任何变化,它的 hash 却改变了。原因是: Webpack 会为每个 chunk 按顺序给出一个依次增加的 chunk id,随着新依赖的增加,chunk 顺序也可能会发生变化,于是 chunk id 也会随之更新;Webpack同样会对 module 使用自增数字命名。
那么我们怎样才能让它在引入新依赖时, 打包输出文件的 hash值 不自动更新呢? 我们可以配置使用 optimization.chunkIds和optimization.moduleIds 解决;
optimization: {
chunkIds: 'named',
moduleIds: 'hashed'
}
但需要注意的是在公共代码 Vendor 内容不变的情况下,添加 entry,或者异步模块,或者external 依赖的时候,Vendor 的 hash 都会改变。
异步模块可以使用魔法注释,在 import 的时候加上 chunkName 的注释,比如这样:
import(/ webpackChunkName: “lodash” / ‘lodash’).then()
这样就有 Name 了;
对于异步模块虽然NamedChunksPlugin 仅对有 name 的 chunk 有效,但是可以通过自定义 nameResolver 的方式来实现我们需要的功能:
new webpack.NamedChunksPlugin(chunk => {
if (chunk.name) {
return chunk.name;
}
return Array.from(chunk.modulesIterable, m => m.id).join("_");
});
对于 external 依赖,我们可以使用 NameAllModulesPlugin 来做一些相关的配置,从而为它命名。
Code Splitting 代码分割
代码分割是 webpack 最引人注目的特性之一。这个特性允许您将代码分割成各种包,然后可以按需加载或并行加载。它不仅可以使打出来的包体积更小,还可以控制资源加载的优先级,如果使用正确,可以很大提升资源加载时间。
以下代码是对于项目中使用到的webpack.optimization的一些参数的解释说明:
optimization: {
runtimeChunk: 'single', // 创建一个运行时文件,以便为所有生成的块共享;设置true 或‘ multiple’:向每个只包含运行时的入口点添加一个额外的块
moduleIds: 'hashed', // 选择模块 id 时使用哪个算法, hashed: 短散列作为 id,以便更好地进行长期缓存
chunkIds: 'named', // 使用可读的块标识符, 避免chunkId自增修改
splitChunks: { // 对于默认已有插件 SplitChunksPlugin 的配置
chunks: 'initial',
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
},
change: {
test: /[\\/]node_modules[\\/](@tarojs[\\/]components)/,
name: 'changes',
chunks: 'all',
priority: 1000,
},
common: {
name: 'common',
chunks: 'all',
minChunks: 2, // 至少被引入的次数,默认是1,引入次数小于2就不会被单独拆分
priority: 120,
},
taro: {
test: /[\\/]node_modules[\\/](nervjs|@tarojs)[\\/]/,
name: 'chunk-taro', // 单独将 taro 拆包
priority: 20 // 权重要大于 vendor 和 app 不然会被打包进 vendor 或者 app
}
}
},
},
参考资料: webpack.js.org/guides/cach… www.jianshu.com/p/6d81adb31… www.thjiang.com/2018/12/30/…