webpack里使用hash的坑

背景:
我在react项目里使用了css modules。按照在webpack中的标准配置流程,需要设置下css-loader:

{
  test: /\.css$/,
  loader: 'style!css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]' 
}

然后很开心的把项目跑了起来,所有的css中的class都加了一段hash。

然后我把代码传到测试服务器上build,惊人的发现css的class有了不同的hash!
然后搜索了下原因,
<blockquote>
[hash] 是根据一个 compilation 对象计算得出的哈希值,如果 compilation 对象的信息不变,则 [hash] 不变
</blockquote>
<blockquote>
compilation 对象代表对某个版本进行一次编译构建的过程,如果在开发模式下(例如用 –watch 检测变化,实时编译),则每次内容变化时会新建一个 complidation,包含了构建所需的上下文信息(构建器配置、文件、文件依赖)。
</blockquote>
简单的说,每次代码改动会造成hash的不同。但老子的代码没改!
然后又看到:
<blockquote>
I can also confirm that hashes are different on different OS. This is a logical consequence since Windows uses backslashes as path separator. It's probably also due to the fact that Windows uses CRLF for new lines. There's nothing we can do about it.
</blockquote>
所以hash还和不同的os相关(我的测试机为ubuntu,本地为windows)。

由于我的代码是前后端渲染,不只和浏览器相关,node本身也要根据react来渲染出一个html出来,而其中className必须和静态css中的className保持一致。当一套代码跑在多个node上时,会存在html中className与css不同的风险。

那么这个问题该如何规避?
我把hash:base64:5直接改成了path。这样就用文件的路径做为class的唯一标识。但不好的地方,是路径可能很长。
在css-loader中,也进行了一段说明,意识是可以自定义吐出的className:
<blockquote>
You can also specify the absolute path to your custom getLocalIdent function to generate classname based on a different schema. Note that this requires webpack >= v2.x. since to be able to pass function in.
</blockquote>

{
  test: /\.css$/,
  use: [
    {
      loader: 'css-loader',
      options: {
        modules: true,
        localIdentName: '[path][name]__[local]--[hash:base64:5]',
        getLocalIdent: (context, localIdentName, localName, options) => {
          return 'whatever_random_class_name'
        }
      }
    }
  ]
}

这里需要webpack版本升级到2。

参考:
Hash changes if a filename is changed
你用 webpack 1.x 输出的 hash 靠谱不?

推荐阅读更多精彩内容