Webpack 配置实践

369 阅读4分钟

我们使用 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'
    }
};
  1. 以上代码中输出的filename配置为全局hash,例如项目中常见的:对 app 模块的任何更改同时也会导致我们的 vendors 模块的 hash 失效,此时我们可以将文件名中把 hash 改成 chunkhash(它只会使用它自己的模块中的内容来生成哈希值),那么更改app 模块中的内容, vendor 模块不会再受影响了。
  2. 但如果我们在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/…