发布于 2026-01-06 0 阅读
0

通过单字母 CSS 类名哈希策略减小包大小

通过单字母 CSS 类名哈希策略减小包大小

通过更改标准 CSS 类名哈希以拆分为单字母名称策略和文件路径,将捆绑包压缩率提高到文件大小的 40%。

我们基于 React 库开发了一个网站,样式方面我们使用了 css-modules。css-modules 的主要思想是,你可以创建BirdCat这两个组件,它们都包含styles.css文件,并在其中设置了一个名为.block 的CSS 类,每个组件的 .block CSS 类都不同。

示例文件:

/* Bird / styles.css */
.block { }
.name { }
/* Cat / styles.css */
.block { }
.name { }
Enter fullscreen mode Exit fullscreen mode

这并非什么高深的技术:在 webpack 中,我们使用 css-loader 处理样式,并设置`localIdentName: "[hash:base64:8]"`。所有类名都会被重命名,并与源文件进行映射,以便区分不同的元素。最简单的情况下,我们使用 `bundle.css` 来存放样式,并使用 `styles.js` 来映射类名(用于加载)。

我们有两个独立的类名,名称很奇怪,例如k3bvEft8

/* Bird */
.k3bvEft8 { }
.f2tp3lA9 { }
/* Cat */
.epIUQ_6W { }
.oRzvA1Gb { }
Enter fullscreen mode Exit fullscreen mode

我们来运行生产版本并压缩文件。例如,我们会看到一个 300KB 的 CSS 文件使用 gzip 压缩后会变成 70KB(使用 Brotli 压缩后会变成 50KB)。这是因为生成哈希值没有被压缩。压缩算法找不到任何序列,并且会记住每个字节的位置。

我们能用这个做什么呢?webpack 在工作时会异步读取文件树并解析类名。你的插件可以从任何文件中获取文件路径和类名,但单个文件内部的顺序会被保留。如果我们能获取文件名并知道文件路径,就可以自己生成哈希值。让我们把 CSS 类名改成只有一个符号的名称,而不是哈希名称。例如,如果我们使用[a-zA-Z]+符号,可以使用 52 位编码。(或者对于[a-zA-Z0-9_-]+符号,可以使用64 位编码,但要注意名称中的第一个数字——你需要一个屏蔽前缀,例如 '_'。)

名称将更改为:

/* Bird */
.a { }
.b { }
/* Cat */
.c { }
.d { }
Enter fullscreen mode Exit fullscreen mode

看起来不错,但是当你使用 webpack 配置运行服务器端时,你可能会以随机顺序获取文件(多亏了异步I/O/读取),你的代码可能会变成这样:

/* Bird */
.c { }
.d { }
/* Cat */
.a { }
.b { }
Enter fullscreen mode Exit fullscreen mode

你好,不匹配!

为了解决这个问题,我们尝试记住从每个路径进入的规则,并为每个文件保存一个本地计数位置。从 0('a' 符号)开始对每个文件进行计数。(第 51 条规则命名为 'Z',第 52 条规则命名为 'ba',依此类推。)

听好了:

/* Bird */
.a { }
.b { }
/* Cat */
.a { }
.b { }
Enter fullscreen mode Exit fullscreen mode

每个文件内部的名称都是唯一的,但是文件数量很多。我们必须从头开始创建一个哈希表,但不是对类名,而是对文件路径

终于得到:

/* Bird */
.a_k3bvEft8 { }
.b_k3bvEft8 { }
/* Cat */
.a_oRzvA1Gb { }
.b_oRzvA1Gb { }
Enter fullscreen mode Exit fullscreen mode

(不需要使用“_”分隔符,因为文件哈希的长度始终是固定的。此示例仅供参考。)

你得到的文件名几乎和之前一样,但这次我们使用的是序列化的类名。例如,原本 50KB 的 CSS 和 47KB 的 JS,我们压缩后得到了 30KB 的 CSS 和 28KB 的 JS(总大小 58KB,已压缩)。
节省了约 40KB,并且性能有了显著提升。

编写一个类来统计条目数并调用css-loader中的 webpack 配置方法,使用getLocalIdent ,非常简单。

PS:如果您想更进一步,创建非常小的名称,可以在 webpack 运行主线程之前创建一个 css 文件树映射,对其进行排序,并将这样的类名命名为一个字母(在这种情况下不要忘记“_”)。

这样可以带来更好的压缩效果:两个文件均为47Kb ,解压后可节省60Kb 。

让网页再次快速起来!


PS:您可以在https://github.com/webpack-contrib/css-loader/issues/1028查看代码(PR:https://github.com/webpack-contrib/css-loader/pull/1181

更新:在生产环境中,我们节省了93% 的*.css 和 *style.js 文件。仅传输解压后71.6Kb1.1Mb文件。

更新 2:由于我们拥有完整的哈希名称映射表,我们可以建议使用 hashlen(文件路径哈希值)来实现最佳性能。例如,生产环境中的 hashlen 设置为 8,但我们将其更改为 4。这使每个页面(html+js+css)节省了约 14kb 的空间。请参阅 PR。


请阅读我的新文章《创建简单的静态服务器组件》


捐赠:以太坊 0x84914da79c22f4aC6fb9D577C73E29E4AaAE7622

文章来源:https://dev.to/denisx/reduce-bundle-size-via-one-letter-css-classname-hash-strategy-10g6