Webpack 缓存

痛定思痛。 2024-04-18 08:24 167阅读 0赞

缓存

使用 webpack 来打包我们的模块化后的应用程序,webpack 会生成一个可部署的 /dist 目录,然后把打包后的内容放置在此目录中。只要把 /dist 目录中的内容部署到服务器上,客户端(通常是浏览器)就能够访问此服务器的网站及其资源。

获取资源是比较耗费时间的,这就是为什么浏览器使用一种名为缓存的技术。

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

也就是说,对于不会经常更新的文件,我们要保证它们的文件名不变,这样就可以命中缓存,浏览器就会直接从本地取出,从而提高性能;对于需要经常更新的文件,我们又要保证它们的文件名,会随着文件内容的改变而改变,这样,浏览器就会从服务器请求更新后的文件。

输出文件的文件名

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

修改 webpack.config.js 文件:

  1. const path = require('path');
  2. const CleanWebpackPlugin = require('clean-webpack-plugin');
  3. const HTMLWebpackPlugin = require('html-webpack-plugin');
  4. module.exports = {
  5. entry: './src/index.js',
  6. plugins: [
  7. new HTMLWebpackPlugin({
  8. title: '缓存'
  9. }),
  10. new CleanWebpackPlugin(['dist'])
  11. ],
  12. output: {
  13. filename: '[name].[chunkhash].js', // 与 chunk 相关的哈希
  14. path: path.resolve(__dirname, 'dist')
  15. }
  16. };

src/index.js 的内容为:

  1. import _ from 'lodash';
  2. function component() {
  3. var element = document.createElement('div');
  4. element.innerHTML = _.join(['Hello', 'webpack'], ' ');
  5. return element;
  6. }
  7. document.body.appendChild(component());

运行 npm run dev,应该产生以下输出:

  1. Hash: f0f47a8eaf7c0a917ac9
  2. Version: webpack 4.0.0
  3. Time: 565ms
  4. Built at: 2018-3-3 21:23:10
  5. Asset Size Chunks Chunk Names
  6. main.dd35d7c54baa3d259f9e.js 550 KiB main [emitted] [big] main
  7. index.html 200 bytes [emitted]
  8. Entrypoint main [big] = main.dd35d7c54baa3d259f9e.js
  9. [./node_modules/webpack/buildin/global.js] (webpack)/buildin/global.js 509 bytes {main} [built]
  10. [./node_modules/webpack/buildin/module.js] (webpack)/buildin/module.js 519 bytes {main} [built]
  11. [./src/index.js] 228 bytes {main} [built]

可以发现,bundle 的名称是它内容(通过 hash)的映射。如果我们不做修改,然后再次运行构建,我们原以为文件名会保持不变。然而,如果我们真的运行,可能会发现情况并非一定如此。

再次运行 npm run dev,输出结果为:

  1. Hash: f0f47a8eaf7c0a917ac9
  2. Version: webpack 4.0.0
  3. Time: 590ms
  4. Built at: 2018-3-3 21:28:55
  5. Asset Size Chunks Chunk Names
  6. main.dd35d7c54baa3d259f9e.js 550 KiB main [emitted] [big] main
  7. index.html 200 bytes [emitted]
  8. Entrypoint main [big] = main.dd35d7c54baa3d259f9e.js
  9. [./node_modules/webpack/buildin/global.js] (webpack)/buildin/global.js 509 bytes {main} [built]
  10. [./node_modules/webpack/buildin/module.js] (webpack)/buildin/module.js 519 bytes {main} [built]
  11. [./src/index.js] 228 bytes {main} [built]

【 输出可能会因当前的 webpack 版本而稍有差异。新版本不一定有和旧版本相同的 hash 问题,但我们以下推荐的步骤,仍然是可靠的。】

缓存第三方库

就像我们之前从代码分离了解到的,CommonsChunkPlugin 可以用于将模块分离到单独的文件中。我们可以将公共库拆分出来,让浏览器对其缓存。

由于在 webpack 4.0.0 中,CommonsChunkPlugin 插件被移除了,而改用 config.optimization.splitChunks。故这里,我们使用 splitChunks。

webpack.config.js

  1. const path = require('path');
  2. const CleanWebpackPlugin = require('clean-webpack-plugin');
  3. const HTMLWebpackPlugin = require('html-webpack-plugin');
  4. module.exports = {
  5. entry: './src/index.js',
  6. plugins: [
  7. new HTMLWebpackPlugin({
  8. title: '缓存'
  9. }),
  10. new CleanWebpackPlugin(['dist'])
  11. ],
  12. output: {
  13. filename: '[name].[chunkhash].js', // 与 chunk 相关的哈希
  14. path: path.resolve(__dirname, 'dist')
  15. },
  16. optimization: {
  17. splitChunks: {
  18. chunks: "initial", // "initial" | "all"(默认就是all) | "async"
  19. name : 'vendor'
  20. // minSize: 0, // 最小尺寸,默认0
  21. // minChunks: 1, // 最小 chunk ,默认1
  22. // maxAsyncRequests: 1, // 最大异步请求数, 默认1
  23. // maxInitialRequests : 1, // 最大初始化请求数,默认1
  24. // cacheGroups:{ // 这里开始设置缓存的 chunks
  25. // // priority: 0, // 缓存组优先级
  26. // vendor: { // key 为entry中定义的 入口名称
  27. // chunks: "initial", // "initial" | "all" | "async"(默认就是异步)
  28. // test: /react|lodash/, // 正则规则验证,如果符合就提取 chunk
  29. // name: "vendor", // 要缓存的 分隔出来的 chunk 名称
  30. // // minSize: 0,
  31. // // minChunks: 1,
  32. // enforce: true,
  33. // // maxAsyncRequests: 1, // 最大异步请求数, 默认1
  34. // // maxInitialRequests : 1, // 最大初始化请求数,默认1
  35. // // reuseExistingChunk: true // 可设置是否重用该chunk(查看源码没有发现默认值)
  36. // }
  37. // }
  38. }
  39. }
  40. };

运行 npm run dev,输出结果为:

  1. Hash: 4f6c4c5f88f59746b875
  2. Version: webpack 4.0.0
  3. Time: 574ms
  4. Built at: 2018-3-3 22:28:36
  5. Asset Size Chunks Chunk Names
  6. main.b7ee0df3852ecc0ea76b.js 5.9 KiB main [emitted] main
  7. vendor.62788e75196910a94bb9.js 547 KiB vendor [emitted] [big] vendor
  8. index.html 277 bytes [emitted]
  9. Entrypoint main [big] = vendor.62788e75196910a94bb9.js main.b7ee0df3852ecc0ea76b.js
  10. [./node_modules/webpack/buildin/global.js] (webpack)/buildin/global.js 509 bytes {vendor} [built]
  11. [./node_modules/webpack/buildin/module.js] (webpack)/buildin/module.js 519 bytes {vendor} [built]
  12. [./src/index.js] 226 bytes {main} [built]

可以发现,第三方库 lodash 被单独分离出来了,保存在 vendor.62788e75196910a94bb9.js 这个 bundle 中。

这种将第三方库(library)(例如 lodash 或 react)提取到单独的 vendor chunk 文件中,是比较推荐的做法,这是因为,它们很少像本地的源代码那样频繁修改。

然后,我们新建 src/print.js 文件:

  1. export default function print(text) {
  2. console.log(text);
  3. };

并在 src/index.js 文件中引入它:

  1. import _ from 'lodash';
  2. import Print from './print';
  3. function component() {
  4. var element = document.createElement('div');
  5. element.innerHTML = _.join(['Hello', 'webpack'], ' ');
  6. element.onclick = Print.bind(null, 'Hello webpack!');
  7. return element;
  8. }
  9. document.body.appendChild(component());

然后,运行 npm run dev,查看各个 bundle 的名称变化。

  1. Hash: d15457d1538e0935727e
  2. Version: webpack 4.0.0
  3. Time: 598ms
  4. Built at: 2018-3-3 22:38:54
  5. Asset Size Chunks Chunk Names
  6. main.4848aa5ef67e9b5b310e.js 6.62 KiB main [emitted] main
  7. vendor.62788e75196910a94bb9.js 547 KiB vendor [emitted] [big] vendor
  8. index.html 277 bytes [emitted]
  9. Entrypoint main [big] = vendor.62788e75196910a94bb9.js main.4848aa5ef67e9b5b310e.js
  10. [./node_modules/webpack/buildin/global.js] (webpack)/buildin/global.js 509 bytes {vendor} [built]
  11. [./node_modules/webpack/buildin/module.js] (webpack)/buildin/module.js 519 bytes {vendor} [built]
  12. [./src/index.js] 317 bytes {main} [built]
  13. [./src/print.js] 65 bytes {main} [built]

可以发现,vendor bundle 的名称一直是 vendor.62788e75196910a94bb9.js,而 main bundle 的名称变了。这正是我们需要的预期结果。

发表评论

表情:
评论列表 (有 0 条评论,167人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Webpack 缓存

    缓存 使用 webpack 来打包我们的模块化后的应用程序,webpack 会生成一个可部署的 /dist 目录,然后把打包后的内容放置在此目录中。只要把 /dist...

    相关 Webpack与浏览器缓存

    必大家都知道浏览器是有缓存的。浏览器缓存分为强缓存和协商缓存,在命中强缓存时就不会像服务器请求数据。 那么就产生了一个问题,在强缓存生效的时间内一旦服务器资源发生了变化...