Daotin

UnoCSS引擎调研

CSS

目前痛点

  1. 之前的版本体验差。项目使用 tailwindcss,由于 tailwind 在v2.1+之后才支持JIT模式,所有很多随意的宽度(比如w-123)啥的不支持,要么在全局样式中的某个位置添加这样一个工具类,要么只能在tailwind.config.js 去定义,但是你又不能穷举所有的数字。
  2. 本地开发会比较慢。tailwind 的PurgeCSS这个清除操作仅在生成构建下有效,而开发环境下仍要使用包含了所有规则巨大的 CSS 文件。这在 Webpack 中表现可能并不明显,但在 Vite 中却有着巨大的影响,毕竟其他内容的加载都非常迅捷。
  3. 性能不是太好。从内部实现上看,Tailwind 依赖于 PostCSS 的 AST 进行修改,并且 Tailwind JIT 依赖于对文件系统的预扫描,并使用文件系统监听器来实现 HMR。文件 I/O 不可避免地会引入开销,而你的构建工具实际上需要再次加载它们。
  4. WindiCSS 已不再维护

UnoCSS 介绍

虽然目前 Tailwind 还不错,但是 UnoCSS 有更大的优势:

  • 直观且完全可定制。像 Tailwind 的一些约定是内置的,你不能随意修改
  • 预设功能。通过预设功能,可以直接支持 Tailwind/WindiCSS…的写法;并且可以共享给他人你的自定义预设。
  • 跳过解析,不使用 AST。性能更好。
  • 其他优势:
    • 纯 CSS 图标
    • 可变修饰 (Variants)
    • 属性化模式 (Attributify Mode)
    • CSS 作用域
    • Shortcuts:多个原子合成一个
    • 网络字体
    • …

UnoCSS和TailWind的区别

UnoCSS 和 Tailwind CSS 都是现代 CSS 解决方案,旨在提高开发效率和体验,但它们在设计理念、功能和工作方式上存在一些关键区别。

1. 核心理念与定位:

  • Tailwind CSS: 是一个功能类优先 (utility-first) 的 CSS 框架。它提供了一整套预设的、低层级的 CSS 功能类,开发者可以直接在 HTML 中组合这些类来构建用户界面,而无需编写太多的自定义 CSS。最新版本的 Tailwind CSS 采用即时 (JIT) 编译器,按需生成 CSS,从而减小最终的 CSS 文件大小。
  • UnoCSS: 更像是一个按需原子化 CSS 引擎 (on-demand atomic CSS engine) 或元框架。它本身不提供任何核心功能类,所有功能都通过预设 (presets) 提供。这意味着你可以选择性地加入你需要的功能,甚至可以创建自己的规范、设计系统和预设。它的核心思想是极致的灵活性和性能。

2. CSS 生成方式:

  • Tailwind CSS: 虽然现代版本使用 JIT 按需生成,但其设计初衷是拥有一套庞大的预定义功能类集。JIT 会扫描你的模板文件,只生成用到的 CSS。
  • UnoCSS: 严格按需生成。它只在你代码中实际用到了某个类时才会生成对应的 CSS 规则。这种方式通常能带来更快的构建速度和更小的最终产物。

我们用一个更具体的例子来说明它们核心理念上的区别,尤其是在 “如何提供和生成CSS” 以及 “灵活性” 这两个方面。

想象一下你要给一个按钮添加样式:红色背景、白色文字、一些内边距。

使用 Tailwind CSS (JIT 模式)

Tailwind CSS 本身提供了一整套预先定义好的功能类名。当你使用它的时候,你是在这些已经存在的类名中进行选择。

html
<button class="bg-red-500 text-white py-2 px-4 rounded">
  Click Me
</button>
  • 核心区别点1:预定义的功能类库
    • bg-red-500, text-white, py-2, px-4, rounded 这些都是 Tailwind CSS 内置定义好的功能类。
    • 在构建时,Tailwind 的 JIT (Just-In-Time) 编译器会扫描你的 HTML 文件,找到你用到的这些类名 (如 bg-red-500),然后只为这些用到的类生成对应的 CSS 规则到最终的 CSS 文件中。
    • 它像一个巨大的工具箱,里面有各种工具(CSS类),你挑着用,JIT 帮你把用到的打包。
  • 核心区别点2:通过配置文件进行定制
    • 如果你想改变 red-500 具体是什么红色,或者想增加一个新的颜色 brand-blue,你会去修改 tailwind.config.js 文件。
    javascript
    // tailwind.config.js
    module.exports = {
      theme: {
        extend: {
          colors: {
            'brand-blue': '#007bff',
          },
        },
      },
      // ...
    }

    然后你就可以用 bg-brand-blue 了。

使用 UnoCSS

UnoCSS 更像是一个 CSS 规则生成引擎。它默认情况下不包含任何具体的 CSS 功能类。你需要通过预设 (presets) 或自定义规则来告诉它如何生成你想要的 CSS。

方式一:使用类似 Tailwind 的预设 (@unocss/preset-wind)

你可以选择一个预设,比如 @unocss/preset-wind,这个预设会使 UnoCSS 的行为方式非常接近 Tailwind CSS。

html
<button class="bg-red-500 text-white py-2 px-4 rounded">
  Click Me
</button>
  • 核心区别点1(此模式下):按需生成,但规则来自预设
    • 当你写 bg-red-500 时,UnoCSS 会查看 @unocss/preset-wind 这个预设。这个预设知道 bg-red-500 应该生成什么样的 CSS(比如 background-color: #ef4444;)。
    • 它不是从一个庞大的预定义库里找,而是根据预设的规则即时生成。这个过程非常快,因为它只处理你用到的东西。

方式二:极致的灵活性 - 自定义规则或使用特殊预设 (例如 Attributify Mode)

这是 UnoCSS 更能体现其“引擎”特性的地方。

  • 使用 Attributify Mode (通过 @unocss/preset-attributify)

    首先,你需要在你的 UnoCSS 配置中启用这个预设:

    javascript
    // uno.config.ts
    import { defineConfig, presetAttributify, presetUno } from 'unocss'
    
    export default defineConfig({
      presets: [
        presetAttributify(), // 启用属性化模式
        presetUno(),         // 一个基础的预设,提供类似 Tailwind 的功能类
      ],
    })

    然后你可以这样写 HTML:

    html
    <button bg="red-500" text="white" p="y-2 x-4" rounded>
      Click Me
    </button>

    或者更简洁地:

    html
    <button bg-red-500 text-white p="y-2 x-4" rounded>
      Click Me
    </button>
    • 核心区别点1:CSS规则的“来源”和“表达方式”更灵活
      • 这里,bg="red-500"、text="white" 不再是 class 属性里的类名,而是直接作为 HTML 元素的属性。
      • UnoCSS 引擎会识别这些属性(根据 presetAttributify 的规则),并动态地为它们生成相应的 CSS。例如,看到 bg="red-500",它会生成 background-color: #ef4444;。
    • 核心区别点2:引擎驱动,而非固定框架
      • UnoCSS 本身不“拥有” bg-red-500 这个概念。是 预设 (preset) 告诉了 UnoCSS 引擎:“当你看到 bg-red-500 这样的模式时,请生成对应的背景颜色 CSS”。
      • 这意味着你可以创建完全自定义的预设或规则,让 UnoCSS 生成任何你想要的 CSS,甚至是你自己发明的简写。比如你可以定义一个规则,让 my-button-style 直接生成上面按钮的所有样式。

总结核心区别的例子:

  • Tailwind CSS: 给你一本厚厚的、包含各种标准工具的说明书和工具箱 (预定义的功能类)。你需要什么样式,就按照说明书去工具箱里找对应的工具(类名)来组合使用。JIT 编译器会帮你把用到的工具打包。你想增加新工具或修改现有工具,主要通过修改其配置文件 (说明书的附录)。
  • UnoCSS: 给你一个高度智能的、可编程的机器人 (CSS 引擎)。
    • 你可以给这个机器人加载不同的 技能包 (presets)。加载了“Tailwind 技能包” (@unocss/preset-wind),它的行为就和 Tailwind 很像。加载了“属性化技能包” (@unocss/preset-attributify),它就能理解直接写在属性里的样式。
    • 你甚至可以教机器人全新的技能 (自定义规则),比如告诉它:“以后看到 crazy-style 这个词,就生成一套特定的 CSS”。
    • 它不依赖于一个固定的“类名清单”,而是根据你提供的规则和实际用到的代码,动态地、即时地生成 CSS。
我可以这样理解吗?Tailwind 是一个“框架”,而UnoCSS 则是生成“框架”的规则。UnoCSS 可以生成Tailwind ,可以生成xxxWind,yyyWind等等

是的,你这样理解在很大程度上是非常准确的!这是一个很好的方式来抓住它们之间的核心区别。

  • Tailwind CSS 是一个“框架”:
  • 它提供了一整套定义好的功能类 (utilities) 和设计系统约定。
  • 你学习并使用这个框架提供的规则和类名。
  • 虽然可配置,但其核心结构和主要功能类是预先设定好的。
  • UnoCSS 更像是生成这些“框架”的“规则引擎”或“元框架”:
  • 它本身不带任何固定的 CSS 类。
  • 你可以通过预设 (presets) 来告诉 UnoCSS 你想遵循什么样的规则。
    • 比如,使用 @unocss/preset-wind,UnoCSS 就能理解并生成与 Tailwind CSS 几乎一样的功能类。所以,是的,UnoCSS 可以“生成”出类似 Tailwind CSS 的功能和体验。
    • 同理,理论上你可以创建或找到一个预设,让 UnoCSS 生成遵循任何你自定义的命名约定和设计系统的 CSS,比如你所说的 “xxxWind” 或 “yyyWind”。这就是它的强大之处——定义规则,然后按需生成。
  • 它提供的是一种机制,让你能够创建、组合或模拟不同的原子化 CSS 框架的风格和功能。

更进一步说:

  • Tailwind CSS 是一个已经为你准备好的、功能齐全的厨房,里面有各种厨具(类名)和食谱(设计规范)。
  • UnoCSS 更像是一个高度可定制的食品加工机器人。你可以给它加载不同的程序(预设):
  • 加载“Tailwind 食谱程序”,它就能做出 Tailwind 风格的菜肴。
  • 加载你自己的“xxxWind 食谱程序”,它就能按你的独特口味来烹饪。
  • 它甚至还能理解一些非常规的操作指令(比如 Attributify 模式,直接在食材上标注做法)。

所以,你的理解“UnoCSS 可以生成 Tailwind,可以生成 xxxWind,yyyWind 等等”是抓住了 UnoCSS 核心的灵活性和引擎特质。它不是简单地“是”一个框架,而是可以“成为”或“驱动”多种框架风格的底层引擎。

何时使用

新项目直接使用 UnoCSS,使用 tailwind 预设,可以直接上手。

老项目的话,tailwind 目前还不是瓶颈,需要的功能都有,暂不需要更新。

Related posts