◆

Windi CSS 简介:按需提取原理与 safelist 白名单

2023-02-02 01:370

Windi CSS 是什么

Windi CSS 是 Tailwind CSS 的一个替代实现,语法基本兼容,主要卖点是构建速度。早期 Tailwind 的做法是先生成全部工具类(几 MB 的 CSS),再靠 PurgeCSS 在生产构建时把没用到的删掉,开发环境下那份巨大的 CSS 会明显拖慢热更新。Windi CSS 反过来做:先扫源码看你用了哪些类,然后只生成这些类,从一开始就不产生多余的东西,所以开发时的 CSS 体积和构建时间都小得多。

需要说明的是,Tailwind 3.0 引入 JIT 引擎之后也改成了按需生成的模式,两者的性能差距已经拉平,Windi CSS 目前也不再活跃维护。所以新项目我会直接选 Tailwind 或 UnoCSS。但下面讲的静态提取原理在这三者身上是完全一致的,踩的坑也一样,理解了在哪个工具上都用得着。

提取

Windi CSS 依靠对源文件进行静态扫描和提取来找到你使用的工具类并为你按需生成相应的 CSS。

这是理解所有原子化 CSS 工具的关键。「静态扫描」意味着它只是把源文件当纯文本读一遍,用正则之类的手段匹配出看起来像工具类的字符串,然后为这些字符串生成 CSS。它不执行你的代码,也不做类型推导,所以任何需要运行时才能确定的类名它都发现不了。

字符串拼接的部分不能被静态地提取:

<div class="text-${ active ? 'green' : 'orange' }-400"></div>

扫描器在这段文本里看到的是 text-、green、orange、-400 这些碎片,拼不出 text-green-400 这个完整的类名,所以对应的 CSS 根本不会被生成。运行时 DOM 上的 class 是对的,但样式表里没有这条规则,表现就是「class 加上了,样式没生效」。这个 bug 特别隐蔽,因为浏览器 DevTools 里能看到 class 存在,很容易往别的方向排查。

需使用工具类的全名来代替:

把三元表达式的分支写成完整类名,扫描器就能各自匹配到 text-green-400 和 text-orange-400,两条规则都会生成:

<div class="${ active ? 'text-green-400' : 'text-orange-400' }"></div>

这条规则可以推广成一句话:类名必须以完整、连续的字符串形式出现在源码里。所以 `text-${color}-400`、'p-' + size、clsx('bg-', theme) 这些写法一律不行;而把完整类名放进对象或数组里是安全的,比如 const map = { ok: 'text-green-400', warn: 'text-orange-400' },因为完整字符串确实出现在文件中了。

白名单

有时,你在必须使用动态拼接:

<div class="p-${size}"></div>

为此,你将需要在 windi.config.js 的配置项 safelist 中列举可能的组合。

import { defineConfig } from 'windicss/helpers';

export default defineConfig({
  safelist: 'p-1 p-2 p-3 p-4',
});

safelist 相当于告诉工具「这几个类不管扫不扫得到,都给我生成」。上面这样写等于把 size 的取值范围限定在 1 到 4,四条规则全部产出,运行时拼出哪个都有对应样式。

要列的组合多时可以用函数动态生成,避免手写一长串:

export default defineConfig({
  safelist: [
    Array.from({ length: 12 }, (_, i) => `p-${i + 1}`),
    ['text-green-400', 'text-orange-400', 'text-red-400'],
  ],
});

用 safelist 有个代价要清楚:这些类无论实际是否用到都会进最终产物,等于放弃了按需的好处。所以别图省事把整个色板都塞进白名单,那和引入完整版 CSS 没区别。数量可控的枚举值(尺寸档位、状态色)用它是合适的,取值范围开放的场景(比如后端返回任意颜色)就不该走工具类,直接写行内 style 更合理。

顺带说一下扫描范围。工具默认只扫特定目录和扩展名,如果类名写在配置文件、Markdown 或者第三方组件库里,可能压根没被扫到。这种情况要在配置的 extract.include 里补上对应路径,光加 safelist 不解决问题。

💬留言板0

还没有留言,来发表第一条吧~

PAGE LOAD / LIVEWindi CSS 简介:按需提取原理与 safelist 白名单加载中