2024-12-25 大改了一下內容,原本寫得太亂。
為什麼選擇 UnoCSS?
我想體驗 UnoCSS 的主要原因,是因為 Tailwind CSS 不支持 Variant Group,而 UnoCSS 支持這個功能。
假如在 Tailwind CSS 中,我需要為某元素添加一些 hover 後的樣式,必須這樣寫:
className="... hover:translate-y-[-5px] hover:scale-101 hover:bg-hotpink hover:border-hotpink"
但如果支持 Variant Group,就可以被精簡為:
className="... hover:(translate-y-[-5px] scale-101 bg-hotpink border-hotpink)"
雖然在字符數量上看起來差別不大,但 可讀性和維護性明顯提高(我主觀認為)。
除了 Variant Group,UnoCSS 的用法與 Tailwind CSS 幾乎無差,因此從 Tailwind CSS 切換到 UnoCSS 是一個相對 無痛的轉換過程。
簡化及複用 Class 的方法
在 UnoCSS 中,可以通過多種方式來簡化 Class 並提升程式碼的可讀性與複用性,包括:
- @apply:可以將多個原子化的樣式提取到一個自定義的 CSS 類中
- Variant Group:Variant Group 支持將相同變體的多個樣式簡化為一組
- 屬性化模式:讓樣式直接以 HTML 屬性形式定義,減少使用傳統的
class
- 前綴自引用:支持通過前綴語法引用自己的樣式,讓程式碼更加簡潔
- 自定義類名
@apply
雖然 UnoCSS 支持
@apply
,但其本質設計仍以按需生成原子化CSS為核心,建議僅在需要大量複用樣式時使用,並保持合理的使用範圍。如果只是為了讓程式碼看起來「整潔」,那其實沒必要用
@apply
,因為原子化樣式就是為了解決維護 CSS 的麻煩。
當 Class 名過長或需要在多處重複使用時,可以通過 @apply 將多個樣式抽取到一個自定義的 CSS 類中,從而提高程式碼的復用性與維護性。
不過,使用 @apply 需要先安裝 Transformer Directives:
npm install -D @unocss/transformer-directives
// or
yarn add -D @unocss/transformer-directives
配置 Transformer Directives
在 uno.config.ts
文件中,引入並啟用 Transformer Directives:
// uno.config.ts
import { defineConfig } from 'unocss'
import transformerDirectives from '@unocss/transformer-directives'
export default defineConfig({
// ...
transformers: [
transformerDirectives(),
],
})
啟用該 Transformer 後,可以支持以下功能:
- @apply:複用樣式。
- @screen:針對不同屏幕尺寸進行響應式設計。
- theme():訪問 UnoCSS 主題的配置。
@apply 使用範例
以下是一個使用 @apply 簡化複用 Class 的範例:
.menu-link-style {
@apply text-white border-solid border-b-3 border-transparent transition ease-in-out pb-1 hover:border-white;
}
<li key={item.name}>
<a href={item.link} className="menu-link-style">
{item.name}
</a>
</li>
補充
Whatever you do, don’t use
@apply
just to make things look “cleaner”. Yes, HTML templates littered with Tailwind classes are kind of ugly. Making changes in a project that has tons of custom CSS is worse.TailwindCSS 官方文檔中提到:不要為了讓程式碼看起來「整潔」就使用
@apply
。一堆 Tailwind class 看起來可能有點亂,但這其實比用@apply
寫一堆自定義 CSS 好得多。為什麼不建議呢?因為過度使用@apply
基本上等同於重新撰寫 CSS,這不僅背離了 Tailwind 的核心設計理念,也會失去使用 Tailwind 的優勢,比如:
- 高效率:直接用 class 就能完成樣式,完全不用切檔案找 CSS。
- 好維護:少了自訂 CSS 的負擔,未來要改樣式也不麻煩。
和 Tailwind 一樣,UnoCSS 的原子化設計是希望讓樣式直觀且開發高效。如果大量使用
@apply
,其實等於是回到傳統 CSS 的寫法,這就背離了它的初衷。https://tailwindcss.com/docs/reusing-styles#extracting-classes-with-apply
Variant Group
Variant Group 支持將相同變體的多個樣式簡化為一組。
假如有以下的 Class:
className="... hover:translate-y-[-5px] hover:scale-101 hover:bg-hotpink hover:border-hotpink"
若想將其簡化為支持 Variant Group 的語法:
className="... hover:(translate-y-[-5px] scale-101 bg-hotpink border-hotpink)"
就需要使用 Variant Group Transformer。
安裝 Variant Group Transformer
可以使用以下命令安裝:
npm install -D @unocss/transformer-variant-group
// or
yarn add -D @unocss/transformer-variant-group
配置 UnoCSS
在 uno.config.ts
文件中,引入並啟用 Transformer:
// uno.config.ts
import { defineConfig } from 'unocss'
import transformerVariantGroup from '@unocss/transformer-variant-group'
export default defineConfig({
// ...
transformers: [
transformerVariantGroup(),
],
})
安裝並配置好 Variant Group Transformer 後,就可以直接在 Class 中使用 Variant Group 語法,大幅提高程式碼的簡潔性與可讀性。
屬性化模式
適合用在簡單的樣式,複雜一點的樣式使用屬性化模式反而會降低可讀性。
啟用屬性化模式
在 uno.config.ts
中加入 presetAttributify()
啟用屬性化模式:
// uno.config.ts
import { defineConfig } from 'unocss'
import presetAttributify from '@unocss/preset-attributify'
export default defineConfig({
presets: [
presetAttributify(),
// ...
],
})
使用範例
直接在標籤上添加屬性來定義樣式:
<!-- 傳統寫法 -->
<button
class="bg-blue-400 hover:bg-blue-500 text-sm text-white font-mono font-light py-2 px-4 rounded border-2 border-blue-200 dark:bg-blue-500 dark:hover:bg-blue-600"
>
Button
</button>
<!-- 屬性化模式 -->
<button
bg="blue-400 hover:blue-500 dark:blue-500 dark:hover:blue-600"
text="sm white"
font="mono light"
p="y-2 x-4"
border="2 rounded blue-200"
>
Button
</button>
支持 TypeScript
新增 shims.d.ts
import type { AttributifyAttributes } from '@unocss/preset-attributify'
declare module 'react' {
interface HTMLAttributes<T> extends AttributifyAttributes {}
}
通常來說類型包括 @unocss/preset-uno
中的常見屬性。如果需要自定義屬性才需要修改。
前綴自引用
UnoCSS 支持通過前綴語法引用自己的樣式,使程式碼更加簡潔。不過個人不太習慣這種寫法,容易看混。
啟用前綴自引用
一樣需要在 uno.config.ts
中加入 presetAttributify()
:
// uno.config.ts
import { defineConfig } from 'unocss'
import presetAttributify from '@unocss/preset-attributify'
export default defineConfig({
presets: [
presetAttributify(),
// ...
],
})
使用範例
當一組樣式中需要部分樣式延續其他的屬性時,可以使用前綴:
{/* 不使用前綴自引用 */}
<button class="border border-red">
Button
</button>
{/* 使用前綴自引用 */}
<button border="~ red">
Button
</button>
自定義類名
UnoCSS 允許自定義類名,而不是僅限於官方提供的規則。
假設要新增一個自定義類名 rounded-xl-shadow
,讓它同時添加圓角和陰影。可以在 uno.config.ts
中添加規則:
// uno.config.ts
import { defineConfig } from 'unocss'
export default defineConfig({
rules: [
['rounded-xl-shadow', {
'border-radius': '1rem',
'box-shadow': '0 4px 6px rgba(0, 0, 0, 0.1)'
}],
],
})
使用範例
直接使用自定義的類名:
<div class="rounded-xl-shadow"></div>
生成的CSS
.rounded-xl-shadow {
border-radius: 1rem;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
集中管理主題樣式
在 defineConfig
中可以使用 theme
屬性,將專案中常用的 顏色、間距、字體大小等樣式集中管理,提升樣式的統一性與可維護性。
定義顏色主題
可以在 uno.config.ts
中集中定義顏色:
// uno.config.ts
import { defineConfig } from 'unocss'
const theme = {
colors: {
'main': '#41289a',
'gray': '#b9b9b9',
'tag': '#52a49a'
}
}
export default defineConfig({
theme,
...
})
使用範例:
<div class="bg-main text-gray border-tag">主題色塊</div>
定義斷點尺寸(breakpoints)
breakpoints
: 集中定義響應式設計中的斷點尺寸,例如:
export default defineConfig({
theme: {
breakpoints: {
sm: '640px', // 小螢幕
md: '768px', // 中螢幕
lg: '1024px', // 大螢幕
},
},
})
使用範例:
<div class="hidden sm:block lg:text-lg">響應式文字</div>
定義字體大小
自定義專案常用的字體大小,統一樣式風格:
export default defineConfig({
theme: {
fontSize: {
small: '0.875rem', // 小字體
base: '1rem', // 基礎字體
large: '1.25rem', // 大字體
},
},
})
使用範例:
<p class="text-base">這是基礎字體大小</p>
<p class="text-large">這是大字體大小</p>
其餘設置可以參考官方文檔 Theme – UnoCSS
編譯 Class
在使用 UnoCSS 時,默認情況下所有的類名會直接顯示在 DOM 中,若要將類名編譯成無語義的哈希值需要手動啟用。
啟用 Class 編譯功能
安裝 Transformer:
npm install -D @unocss/transformer-compile-class
// or
yarn add -D @unocss/transformer-compile-class
在 uno.config.ts
中配置 Transformer:
// uno.config.ts
import { defineConfig } from 'unocss'
import transformerCompileClass from '@unocss/transformer-compile-class'
export default defineConfig({
transformers: [
transformerCompileClass(),
],
})
在需要編譯的類名前加上特定的標識,默認是 :uno:
:
<main className=":uno: mt-[3.75rem] lg:mt-[5.6rem] xl:mt-[6.25rem]">
...
</main>
編譯後的效果
啟用 transformer-compile-class
並在指定的 Class 前加上 :uno:
後,UnoCSS 會將這些類名編譯成隨機的哈希值。例如:
自定義編譯標識
如果不想使用默認的 :uno:
,可以在 uno.config.ts
中自定義編譯標識:
trigger
: 需要編譯的標識classPrefix
: 編譯後的類名前綴
import { defineConfig } from 'unocss'
import transformerCompileClass from '@unocss/transformer-compile-class'
export default defineConfig({
transformers: [
transformerCompileClass({
classPrefix: 'custom-prefix-',
trigger: ':custom-trigger:',
}),
],
})
使用範例:
<!-- 編譯前 -->
<div className=":custom-trigger: rounded-xl-shadow">test</div>
<!-- 編譯後 -->
<div class="my-prefix-p5v6u8">test</div>
動態樣式的編譯
transformerCompileClass
是設計來將靜態類名轉換為哈希值,這樣有助於壓縮和隱藏 CSS 實現。動態類名需要在運行時解析和應用,這些類名是根據運行時的邏輯條件生成的,因此無法像靜態類名那樣編譯。
<div className={`:uno: flex h-full ${index === 0 && 'relative md:flex-col'}`}>
...
</div>
如果希望動態樣式一樣也能被編輯,也不是沒有辦法,只是稍微麻煩了點。
使用變數
將條件判斷邏輯提取到變數 divClass
中,這樣可以確保動態類名的部分也能被編譯
const divClass = index === 0 ? ':uno: flex h-full relative md:flex-col' : ':uno: flex h-full'
<div className={divClass}>
...
</div>
学习