重新構想原子化 CSS

感謝印記中文的 QC-L[1] 對本文進行翻譯,英文原文: English Version[2]

本文會比往期文章相對長些。這是我個人的一個重大的工具發布,有許多內容我想談論。如果你能花些時間讀完,不勝感激,希望能對你有所幫助 :)

推薦訪問?https://antfu.me/posts/reimagine-atomic-css-zh 以獲得最好的閱讀體驗

什么是原子化 CSS?

首先,讓我們為 原子化 CSS (Atomic CSS) 給出適當的定義:

John Polacek 在 文章 Let’s Define Exactly What Atomic CSS is[3] 中寫道:

Atomic CSS is the approach to CSS architecture that favors small, single-purpose classes with names based on visual function.

譯文:

原子化 CSS 是一種 CSS 的架構方式,它傾向于小巧且用途單一的 class,并且會以視覺效果進行命名。

有些人可能會稱其為函數式 CSS,或者 CSS 實用工具。本質上,你可以將原子化的 CSS 框架理解為這類 CSS 的統稱:

.m-0?{margin:?0;
}
.text-red?{color:?red;
}
/*?...?*/

市面上有不少實用至上的 CSS 框架,如 Tailwind CSS[4],Windi CSS[5] 以及 Tachyons[6] 等。

同時有些 UI 庫也會附帶一些 CSS 工具類作為框架的補充,如 Bootstrap[7] 和 Chakra UI[8]

這篇文章并不打算和你討論使用原子化 CSS 的優缺點,相信你己經在各種地方聽到了許多討論。今天我們將從框架作者的角度來聊聊如何權衡設計以構建出那些你喜歡的框架,它們的局限性,以及如何能將它們做得更好以使得我們的日常工作受惠。

背景

在正式開始前,先來聊聊背景。如果你還不認識我,我叫 Anthony Fu,是 Vite[9] 團隊的成員,也是 Vitesse[10] (Vite 社區最受歡迎的起手模板之一) 的作者。我享受原子化 CSS 帶來的快速開發體驗,而因此選擇了 Tailwind CSS[11] 作為 Vitesse 的默認 UI 框架。雖然 Vite 較 Webpack 等工具相比,在加載速度上有了大幅提升,但由于 Tailwind 生成了數 MB 的 CSS,使得加載與更新 CSS 成為了整個 Vite 應用的性能瓶頸。我曾以為這是使用為了原子式 CSS 的一種權衡,直到我發現了 Windi CSS[12]

5f664629a8e5c149cb84f98c486060ce.png

Windi CSS[13] 是從零開始編寫的 Tailwind CSS 的替代方案。它的零依賴,也不要求用戶安裝 PostCSS 和 Autoprefixer。更為重要的是,它支持 按需生成。Windi CSS 不會一次生成所有的 CSS,而是只會生成你在代碼中實際使用到的原子化 CSS。這與 Vite 按需使用的理念不謀而合,也因此,我為它編寫了 一個 Vite 插件[14]。不出所料,從一個簡單的測試上可以看到它比 Tailwind 要快了 20~100 倍[15]

項目進展相當順利,Windi CSS 也快速成長為一個團隊,我們也引入了許多創新,如 自動值推導[16],可變修飾組[17],Shortcuts[18],在 DevTools 中進行設計[19],屬性化模式[20] 等。作為結果,Tailwind 也 因此[21] 使用了同樣的技術并推出了自己的 JIT 按需引擎[22]

剖析原子化 CSS

在文章開始前,我們來聊聊原子化 CSS 的工作原理。

傳統方案

制作原子化 CSS 的傳統方案其實就是提供所有你可能需要用到的 CSS 工具。例如,你可能會用預處理器(這里選用的是 SCSS)生成如下代碼:

// style.scss@for $i from 1 through 10 {.m-#{$i} {margin: $i / 4 rem;}
}

編譯結果為:

.m-1?{?margin:?0.25?rem;?}
.m-2?{?margin:?0.5?rem;?}
/*?...?*/
.m-10?{?margin:?2.5?rem;?}

現在你可以直接使用 class="m-1" 來設置邊距。但正如你所見,用這種方法的情況下,你不能使用除了 1 到 10 之外的邊距,而且,即使你只使用了其中一條 CSS 規則,但還是要為其余幾條規則的文件體積買單。如果之后你還想支持不同的 margin 方向,使用比如 mt 代表 margin-topmb 代表 margin-bottom 等,加上這 4 個方向以后,你的 CSS 大小會變成原來的 5 倍。如果再有使用到像 :hover:focus 這樣的偽類時,體積還會得更變大。以此類推,每多加一個工具類,往往意味著你 CSS 文件的大小也會隨之增加。這也就是為什么傳統的 Tailwind 生成的 CSS 文件會有數 MB 的大小。

為了解決這個問題,Tailwind 通過使用 PurgeCSS[23] 來掃描你的大包產物并刪除你不需要的規則。這得以使其在生產環境中 CSS 文件縮減為幾 KB。然而,請注意,這個清除操作僅在生成構建下有效,而開發環境下仍要使用包含了所有規則巨大的 CSS 文件。這在 Webpack 中表現可能并不明顯,但在 Vite 中卻有著巨大的影響,畢竟其他內容的加載都非常迅捷。

既然生成再清除的方法存在局限性,那是否有更好的解決方案?

按需生成

"按需生成" 的想法引入了一種全新的思維方式。讓我們先來對比下這些方案:

04634ebee7c136d2da3ea98bf7597462.png

傳統的方式不僅會消耗不必要的資源(生成了但未使用),甚至有時更是無法滿足你的需求,因為總會有部分需求無法包含在內。

3462263e6173da257033a70caad04df5.png

通過調換 "生成" 和 "掃描" 的順序,"按需" 會為你節省浪費的計算開銷和傳輸成本,同時可以靈活地實現預生成無法實現的動態需求。另外,這種方法可以同時在開發和生產中使用,提供了一致的開發體驗,使得 HMR (Hot Module Replacement, 熱更新) 更加高效。

為了實現這一點,Windi CSS 和 Tailwind JIT 都采用了預先掃描源代碼的方式。下面是一個簡單示例:

import?glob?from?'fast-glob'
import?{?promises?as?fs?}?from?'fs'//?通常這個是可以配置的
const?include?=?['src/**/*.{jsx,tsx,vue,html}']async?function?scan()?{const?files?=?await?glob(include)for?(const?file?of?files)?{const?content?=?await?fs.readFile(file,?'utf8')//?將文件內容傳遞給生成器并配對?class?的使用情況}
}await?scan()
//?掃描會在構建/服務器啟動前完成
await?buildOrStartDevServer()

為了在開發期間提供 HMR,通常會啟動一個 文件系統監聽器[24]

import?chokidar?from?'chokidar'chokidar.watch(include).on('change',?(event,?path)?=>?{//?重新讀取文件const?content?=?await?fs.readFile(file,?'utf8')//?將新的內容重新傳遞給生成器//?清除?CSS?模塊的緩存并觸發?HMR?事件
})

因此,通過按需生成方式,Windi CSS 獲得了比傳統的 Tailwind CSS 快 100 倍左右[25] 的性能。

痛癢

我現在在我幾乎所有的應用中都在使用 Windi CSS,而且效果顯著,性能優異,HMR 瞬間完成幾乎無法察覺。自動值推導[26] 和 屬性化模式[27] 更是提升了我的開發體驗。到這里,我本該可以睡上一個好覺去想想其他事情了,但是有時候,它還是會瘙你癢癢打擾你的美夢。

我發現最令人討厭的是,和很多時候我不清楚我得到的結果是什么,以及怎么樣做才能讓它生效。對我而言,最好最理想的原子化 CSS 應該是直覺性的。一旦學會,它應該非常直觀易懂,并且可以推導出其他已知情況。如果你知道 mt-1 是上邊距的一倍單位,你就會直覺地認為 mb-2 是下邊距的兩倍單位。當它正常工作時,是直覺使然,但當它不起作用時,會令人沮喪和困惑。

例如,我們知道 Tailwind 中的 border-2 標識邊框寬度為 2px4 表示 4px6 表示 6px8 表示 8px,但當你使用 border-10 卻不起作用(你甚至需要一些時間來發現這件事!)。你可能會說這是故意而為之,以使得設計系統具有一致性。不如這樣,我們來做個小測驗,如果想要讓 border-10 正常工作,應該如何做?

在你的全局樣式中的某個位置添加這樣一個工具類?

.border-10?{border-width:?10px;
}

快速且直觀,最重要的是,它的確解決了你的需求。但是,如果什么都需要我自己手動添加,那我們為什么還需要使用 Tailwind ?

如果你對 Tailwind 了解深入一些,那你可能知道它可以進行額外配置。所以你需要花 5 分鐘,檢索他們的文檔。你將得到如下方案[28]

//?tailwind.config.js
module.exports?=?{theme:?{borderWidth:?{DEFAULT:?'1px','0':?'0','2':?'2px','3':?'3px','4':?'4px','6':?'6px','8':?'8px','10':?'10px'?//?<--?here}}
}

這似乎很合理,我可以把我需要的情況都列出來,回去繼續工作了...等一下,我剛剛進行到哪里了?因為這樣一個工具的丟失而被打斷,除了配置,我們還會需要時間重新找回原本正在進行的工作的上下文。接著,如果我想設置邊框顏色,我還需要查詢文檔,然后如何進行配置。也許有人喜歡這樣的工作流程,但這并不適合我,我并不享受被本該直覺性工作的工具打斷的我的工作流程。

Windi CSS 對規則相對寬松一些,會盡可能地根據你使用的 class 提供相應的實用工具類。在這種情況下,border-10 在 Windi 中可以做到開箱即用。但是,由于 Windi 需要與 Tailwind 兼容,它還必須使用與 Tailwind 完全相同的配置項。盡管數字推斷的問題得到了解決,但如果你想添加一些自定義的工具,這將是一場噩夢。下面是一個來自 Tailwind 文檔[29] 的示例:

//?tailwind.config.js
const?_?=?require('lodash')
const?plugin?=?require('tailwindcss/plugin')module.exports?=?{theme:?{rotate:?{'1/4':?'90deg','1/2':?'180deg','3/4':?'270deg',}},plugins:?[plugin(function({?addUtilities,?theme,?e?})?{const?rotateUtilities?=?_.map(theme('rotate'),?(value,?key)?=>?{return?{[`.${e(`rotate-${key}`)}`]:?{transform:?`rotate(${value})`}}})addUtilities(rotateUtilities)})]
}

將產生如下代碼:

.rotate-1\/4?{transform:?rotate(90deg);
}
.rotate-1\/2?{transform:?rotate(180deg);
}
.rotate-3\/4?{transform:?rotate(270deg);
}

產生的 CSS 代碼甚至比結果還要長。并且難以閱讀和維護,同時,它破壞了按需應變的能力。

Tailwind 的 API 和插件系統沿用了舊的思維方式進行設計,并不能適應新的按需方式。其核心工具是在生成器中鍛造出來的,而且其定制化功能相當有限。因此,我開始思考,如果我們可以放棄這些歷史包袱,并以隨需應變思想重新設計它,我們可以得到什么?

向你介紹 UnoCSS

**UnoCSS**[30] - 具有高性能且極具靈活性的即時原子化 CSS 引擎。

該項目誕生于我在國慶期間的做的一些隨機實驗。從使用者的角度出發去探索靈活性和直觀性的最佳平衡,加上按需生成的思想,這些實驗的最終結果在不少方面甚至超出了我的預期。接下來讓我為你逐一介紹:

引擎

UnoCSS 是一個引擎,而非一款框架,因為它并未提供核心工具類,所有功能可以通過預設和內聯配置提供。

我們設想 UnoCSS 能夠通過預設模擬大多數已有原子化 CSS 框架的功能。也有可能會被用作創建一些新的原子化 CSS 框架的引擎。例如:

import?UnocssPlugin?from?'@unocss/vite'//?以下預設目前還不存在,
//?歡迎大家踴躍貢獻!
import?PresetTachyons?from?'@unocss/preset-tachyons'
import?PresetBootstrap?from?'@unocss/preset-bootstrap'
import?PresetTailwind?from?'@unocss/preset-tailwind'
import?PresetWindi?from?'@unocss/preset-windi'
import?PresetAntfu?from?'@antfu/oh-my-cool-unocss-preset'export?default?{plugins:?[UnocssPlugin({presets:?[//?PresetTachyons,PresetBootstrap,//?PresetTailwind,//?PresetWindi,//?PresetAntfu//?選擇其中一個...或多個!]})]
}

讓我們來看看如何使它們成為可能:

直觀且完全可定制

UnoCSS 的主要目標是直觀性和可定制性。它可以讓你在數十秒內,定義你自己的 CSS 工具。

靜態規則

原子化 CSS 可能數量相當龐大。因此,規則定義直接了當對于閱讀和維護非常重要。如需為 UnoCSS 創建一個自定義規則,你可以這樣寫:

rules:?[['m-1',?{?margin:?'0.25rem'?}]
]

當在用戶代碼庫中檢測到 m-1 時,就會產生如下 CSS:

.m-1?{?margin:?0.25rem;?}
動態規則

想要使其動態化,可以將匹配器修改為正則表達式,將主體改為一個函數:

rules:?[[/^m-(\d)$/,?([,?d])?=>?({?margin:?`${d?/?4}rem`?})],[/^p-(\d)$/,?(match)?=>?({?padding:?`${match[1]?/?4}rem`?})],
]

其中,主題函數的第一個參數為匹配結果,所以你可以對它進行解構以獲得正則表達式的匹配組。

例如,當你使用:

<div?class="m-100"><button?class="m-3"><icon?class="p-5"?/>My?Button</button>
</div>

就會產生相應的 CSS:

.m-100?{?margin:?25rem;?}
.m-3?{?margin:?0.75rem;?}
.p-5?{?padding:?1.25rem;?}

這樣就行了。而現在,你只需要使用相同的模式添加更多的實用工具類,你就擁有了屬于自己的原子化 CSS!

可變修飾

可變修飾 (Variants)[31] 在 UnoCSS 中也是簡單且強大的。這里有幾個示例:

variants:?[//?支持所有規則的?`hover:`{match:?s?=>?s.startsWith('hover:')???s.slice(6)?:?null,selector:?s?=>?`${s}:hover`,},//?支持?`!`?前綴,使規則優先級更高{match:?s?=>?s.startsWith('!')???s.slice(1)?:?null,rewrite:?(entries)?=>?{//?在所有?CSS?值中添加?`?!important`entries.forEach(e?=>?e[1]?+=?'?!important')return?entries},}
],

你可以參考 文檔[32] 了解更多細節。

預設

你可以將自己的自定義規則和可變修飾打包成預設,與他人分享,或是使用 UnoCSS 作為引擎創建你自己的原子化 CSS 框架!

同時,我們在發布時也提供了 一些預設[33] 供你快速上手。

值得一提的是,默認的 `@unocss/preset-uno`[34] 預設(實驗階段)是一系列流行的原子化框架的 通用超集,包括了 Tailwind CSS,Windi CSS,Bootstrap,Tachyons 等。

例如,ml-3(Tailwind),ms-2(Bootstrap),ma4(Tachyons),mt-10px(Windi CSS)均會生效。

.ma4?{?margin:?1rem;?}
.ml-3?{?margin-left:?0.75rem;?}
.ms-2?{?margin-inline-start:?0.5rem;?}
.mt-10px?{?margin-top:?10px;?}

了解更多關于默認預設的信息[35]

靈活性

截止目前為止,我們都在向你展示如何使用 UnoCSS 來模仿 Tailwind 和其他原子化框架的行為,即便 UnoCSS 讓這件事變得十分容易,但僅此一點可能也不會在最終使用者的方面產生太大影響。

一起來見識下 UnoCSS 真正的威力:

屬性化模式

屬性化模式 (Attributify Mode)[36] 是 Windi CSS 最受歡迎的特性之一。它能幫助你通過使用屬性更好地組織和分組你的實用工具類。

它會把你的冗長的 Tailwind 代碼(難以閱讀與編輯):

<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>

在更好的按類型進行組織的同時,也節省了重復輸入相同前綴的時間。

在 UnoCSS 中,我們也實現了屬性化模式,只使用 **一個可變修飾**[37] 和 **一個提取器**[38],總共 代碼行數不超過 100!更重要的是,它直接適用于你自定義的任何規則!

除了 Windi CSS 的屬性化模式,僅需改動幾行代碼,我們還實現了無值的屬性的支持:

<div?class="m-2?rounded?text-teal-400"?/>

現在變為:

<div?m-2?rounded?text-teal-400?/>

整個屬性化模式是通過 `@unocss/preset-attributify`[39] 預設提供的,詳細的使用方法請參考其文檔。

純 CSS 圖標

如果你讀過我之前的文章 Journey with Icons Continues[40],你一定知道我對圖標非常熱衷,并且在積極研究圖標的各種解決方案。這次,憑借 UnoCSS 的靈活性,我們甚至可以擁有純 CSS 的圖標。你沒看錯,它是純 CSS 的圖標,不需要任何 JavaScript!讓我們來看看它長什么樣子:

<!--?A?basic?anchor?icon?from?Phosphor?icons?-->
<div?class="i-ph-anchor-simple-thin"?/>
<!--?An?orange?alarm?from?Material?Design?Icons?-->
<div?class="i-mdi-alarm?text-orange-400?hover:text-teal-400"?/>
<!--?A?large?Vue?logo?-->
<div?class="i-logos-vue?text-3xl"?/>
<!--?Sun?in?light?mode,?Moon?in?dark?mode,?from?Carbon?-->
<button?class="i-carbon-sun?dark:i-carbon-moon"?/>
<!--?Twemoji?of?laugh,?turns?to?tear?on?hovering?-->
<div?class="i-twemoji-grinning-face-with-smiling-eyes?hover:i-twemoji-face-with-tears-of-joy"?/>
6a4f7e20b0e303942c704d34ff769cdd.gif

與可變修飾結合,你甚至可以根據懸停狀態或顏色模式來切換圖標。得益于 Iconify[41] 項目,你可以從一百余個熱門圖標集合(Material Design Icons, Ant Design Icons 等等)中獲得 超過一萬個圖標 供你按需使用。

同樣的,這個功能的實現代碼并未超過 100 行。具體請參考 `@unocss/preset-icons`[42] 預設的實現了解其中的魔法。

希望這些預設可以讓你對 UnoCSS 的靈活性有一個大致的了解。它還處于一個非常早期的階段,有很多可能性等待我們去探索。

CSS 作用域

在使用 Tailwind / Windi 時,我遇到的另一個問題就是 樣式預檢 (Preflight)[43]。預檢功能重置了原生元素,并為 CSS 變量提供了一些兜底方案,在開發一個只使用 Tailwind/Windi 的新應用時,效果很棒。但當你想讓它們與其他 UI 框架一起工作,或者使用 Tailwind 編寫一些共享組件時,預檢往往會引入許多沖突,破壞你現有的 UI。

因此,UnoCSS 采取了另一個霸氣的操作,不支持預檢。相反,它將 CSS 重置的控制權完全留給了用戶 (或 UnoCSS 上層框架),讓他們使用適合自己的方案 - Normalize.css,Reset.css 或者 UI 框架自帶的CSS 重置等。

這也使得 UnoCSS 在 CSS 作用域上有了更多可能性。例如,我們在 Vite 插件上有一個實驗性的 scoped-vue 模式,可以為每個組件生成作用域樣式,你可以安全地使用原子化 CSS 作為組件庫,而無需擔心與用戶的 CSS 發生沖突。比如:

<template><div?class="m-2?rounded"><slot></div>
<template><!--?以下內容將被注入?bundler?中?-->
<style?scoped>
.m-2{margin:0.5rem;}
.rounded{border-radius:0.25rem;}
</style>

我們還在嘗試更多的可能性,比如支持 Web Component,MPA 情況下的 CSS 代碼分割,以及模塊級別的 CSS 作用域等。

性能

考慮到 UnoCSS 帶來的靈活性和想象力,坦率地說,我認為性能可能不是那么重要的事情。出于好奇,我寫了一個 簡單的 benchmark[44] 來比較性能。結果令人驚訝:

10/21/2021,?2:17:45?PM
1656?utilities?|?x50?runsnone????????????????????????????8.75?ms?/????0.00?ms?
unocss???????v0.0.0????????????13.72?ms?/????4.97?ms?(x1.00)
windicss?????v3.1.9???????????980.41?ms?/??971.66?ms?(x195.36)
tailwindcss??v3.0.0-alpha.1??1258.54?ms?/?1249.79?ms?(x251.28)

從結果來看,UnoCSS 可以比 Tailwind 的 JIT 引擎快 200 倍!說實話,在按需生成的情況下,Windi 和 Tailwind JIT 都已經算是超快了,UnoCSS 的性能提升感知度可能沒有那么高。然而,幾乎為零的開銷意味著你可以將 UnoCSS 整合到你現有的項目中,作為一個增量解決方案與其他框架一同協作,而不需要擔心性能損耗。

在開發時,UnoCSS 做了很多性能上的優化。如果你感興趣,可以參考:

跳過解析,不使用 AST

從內部實現上看,Tailwind 依賴于 PostCSS 的 AST 進行修改,而 Windi 則是編寫了一個自定義解析器和 AST。考慮到在開發過程中,這些工具 CSS 的并不經常變化,UnoCSS 通過非常高效的字符串拼接來直接生成對應的 CSS 而非引入整個編譯過程。同時,UnoCSS 對類名和生成的 CSS 字符串進行了緩存,當再次遇到相同的實用工具類時,它可以繞過整個匹配和生成的過程。

單次迭代

正如前文所述,Windi CSS 和 Tailwind JIT 都依賴于對文件系統的預掃描,并使用文件系統監聽器來實現 HMR。文件 I/O 不可避免地會引入開銷,而你的構建工具實際上需要再次加載它們。那么我們為什么不直接利用已經被工具讀取過的內容呢?

除了獨立的生成器核心以外,UnoCSS 有意只提供了 Vite 插件(以后可能考慮其他的集成),這使得它能夠專注于與 Vite 的最佳集成。

在 Vite 中,transform 的鉤子將與所有的文件及其內容一起被迭代。因此,我們可以寫一個插件來收集它們,比如:

export?default?{plugins:?[{name:?'unocss',transform(code,?id)?{//?過濾掉無需掃描的文件if?(!filter(id))?return//?掃描代碼(同時也可以處理開發中的無效內容)scan(code,?id)//?我們只需要內容,所以不需要對代碼進行轉換return?null},resolveId(id)?{return?id?===?VIRTUAL_CSS_ID???id?:?null},async?load(id)?{//?生成的?css?會作為一個虛擬模塊供后續使用if?(id?===?VIRTUAL_CSS_ID)?{return?{?code:?await?generate()?}}}}]
}

由于 Vite 也會處理 HMR,并在文件變化時再次執行 transform 鉤子,這使得 UnoCSS 可以在一次加載中就完成所有的工作,沒有重復的文件 I/O 和文件系統監聽器。此外,通過這種方式,掃描會依賴于模塊圖而非文件 glob。這意味著只有構建在你應用程序中的模塊才會影響生成的 CSS,而并非你文件夾下的任何文件。

我們還做了一些小技巧來提高性能。我可能會在以后再寫一篇關于它們的文章,但在此之前,你可以通過閱讀代碼來弄清它們 :)

現在能用嗎?

簡而言之:可以,但有注意事項。

UnoCSS 仍處于實驗階段,但由于其精簡的設計,生成的結果已經非常可靠了。需要注意的一點是,API 還沒有最終定案,雖然我們會遵循 semver 的進行版本發布,但是還請為破壞性改動做好準備。

注意:它并非被設計成 Windi CSS 或 Tailwind 的替代品(考慮等待 Windi CSS v4)。我們不建議將現有項目完全遷移到 UnoCSS。你可以在新的項目中試用它,或者將它作為你現有 CSS 框架的補充(例如,禁用默認預設,只使用純 CSS 圖標的預設,或者自定義你的規則)。

順便說一句,目前 你正在閱讀的網站[45] 就構建于 UnoCSS 之上,供你參考 :P。

同時,歡迎分享你正在制作的預設或幫助我們貢獻默認的預設。期待看到你能夠蹦出什么新想法!

關于 Windi CSS?

作為 Windi CSS 的團隊成員,我與 Windi CSS 的創作者 Voorjaar[46] 緊密合作。你可以認為 UnoCSS 是 Windi CSS 團隊的一個激進的實驗,如果進展順利,它可能成為 Windi CSS v4 的新引擎

Windi CSS 作為一個框架,將填補 UnoCSS 作為一個引擎有意不提供的 @apply 預處理,IDE 智能提示,預處理等功能的缺失。而且它還將利用 UnoCSS 為核心工具為用戶配置提供高性能和靈活性。

在我們為 Windi v4 嵌入新引擎之前,一個使用 UnoCSS 作為 Windi CSS 擴展的 npm 包(例如,擁有純 CSS 圖標)將很快發布。敬請關注 :)

結束語

非常感謝你的閱讀!如果你對它感興趣,請記得查看 `antfu/unocss`[47] 倉庫以了解更多細節,也可以通過 **在線 Playground**[48] 進行嘗試。

歡迎評論或轉發 此推文[49],讓我知道你的想法!🙌

參考資料

[1]

QC-L: https://github.com/QC-L

[2]

English Version: /posts/reimagine-atomic-css

[3]

文章 Let’s Define Exactly What Atomic CSS is: https://css-tricks.com/lets-define-exactly-atomic-css/

[4]

Tailwind CSS: https://tailwindcss.com/

[5]

Windi CSS: https://cn.windicss.org/

[6]

Tachyons: https://tachyons.io/

[7]

Bootstrap: https://getbootstrap.com/docs/5.1/utilities/api/

[8]

Chakra UI: https://chakra-ui.com/docs/features/style-props

[9]

Vite: https://vitejs.dev/

[10]

Vitesse: https://github.com/antfu/vitesse

[11]

Tailwind CSS: https://tailwindcss.com/

[12]

Windi CSS: https://cn.windicss.org

[13]

Windi CSS: https://cn.windicss.org

[14]

一個 Vite 插件: https://github.com/windicss/vite-plugin-windicss

[15]

20~100 倍: https://twitter.com/antfu7/status/1361398324587163648

[16]

自動值推導: https://cn.windicss.org/features/value-auto-infer.html

[17]

可變修飾組: https://windicss.org/features/variant-groups.html

[18]

Shortcuts: https://windicss.org/features/shortcuts.html

[19]

在 DevTools 中進行設計: https://twitter.com/antfu7/status/1372244287975387145

[20]

屬性化模式: https://twitter.com/windi_css/status/1387460661135896577

[21]

因此: https://twitter.com/adamwathan/status/1371542711086559237?s=20

[22]

JIT 按需引擎: https://tailwindcss.com/docs/just-in-time-mode

[23]

PurgeCSS: https://purgecss.com/

[24]

文件系統監聽器: https://github.com/paulmillr/chokidar

[25]

快 100 倍左右: https://twitter.com/antfu7/status/1361398324587163648

[26]

自動值推導: https://cn.windicss.org/features/value-auto-infer.html

[27]

屬性化模式: https://twitter.com/windi_css/status/1387460661135896577

[28]

如下方案: https://tailwindcss.com/docs/border-width#border-widths

[29]

Tailwind 文檔: https://tailwindcss.com/docs/plugins#escaping-class-names

[30]

UnoCSS: https://github.com/antfu/unocss

[31]

可變修飾 (Variants): https://cn.windicss.org/utilities/variants.html#variants

[32]

文檔: https://github.com/antfu/unocss#custom-variants

[33]

一些預設: https://github.com/antfu/unocss#presets

[34]

@unocss/preset-uno: https://github.com/antfu/unocss/tree/main/packages/preset-uno

[35]

了解更多關于默認預設的信息: https://github.com/antfu/unocss/tree/main/packages/preset-uno

[36]

屬性化模式 (Attributify Mode): https://windicss.org/posts/v30.html#attributify-mode

[37]

一個可變修飾: https://github.com/antfu/unocss/blob/main/packages/preset-attributify/src/variant.ts

[38]

一個提取器: https://github.com/antfu/unocss/blob/main/packages/preset-attributify/src/extractor.ts

[39]

@unocss/preset-attributify: https://github.com/antfu/unocss/blob/main/packages/preset-attributify

[40]

Journey with Icons Continues: /posts/journey-with-icons-continues

[41]

Iconify: https://iconify.design/

[42]

@unocss/preset-icons: https://github.com/antfu/unocss/blob/main/packages/preset-icons

[43]

樣式預檢 (Preflight): https://tailwindcss.com/docs/preflight

[44]

簡單的 benchmark: https://github.com/antfu/unocss/tree/main/bench

[45]

你正在閱讀的網站: https://github.com/antfu/antfu.me

[46]

Voorjaar: https://github.com/voorjaar

[47]

antfu/unocss: https://github.com/antfu/unocss

[48]

在線 Playground: https://unocss.antfu.me/

[49]

此推文: https://twitter.com/antfu7/status/1452802545118711812

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/275298.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/275298.shtml
英文地址,請注明出處:http://en.pswp.cn/news/275298.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

cv::mat 顏色空間_網站設計基礎:負空間

cv::mat 顏色空間Let’s start off by answering this question: What is negative space? It is the “empty” space between and around the subjects of an image. In the context of web design, your “subjects” are the pictures, videos, text, buttons and other e…

MVC3 上傳文件

前臺引擎采用Razor 上傳頁View&#xff1a; model System.Web.HttpContextBase{ ViewBag.Title "上傳文件";}<h2>上傳文件</h2><br /><br />*new { enctype "multipart/form-data" }比不可少&#xff0c;否則上傳文件不會成功…

Day07 - Ruby比一比:Symbol符號與String字串

前情提要&#xff1a; 第六天我們透過Ruby代碼練習public&#xff0c;protected和privatemethod時&#xff0c;發現冒號在前面的參數&#xff0c;&#xff1a;mydraft&#xff0c;&#xff1a;myspace&#xff0c;這些就是符號Symbol。在今天&#xff0c;我們就來解釋Symbol吧&…

[知乎回答] 前端是否要學習 Node.js?

大家好&#xff0c;我是若川。最近組織了源碼共讀活動&#xff0c;感興趣的可以加我微信 ruochuan12很多小伙伴都表示收獲頗豐。一起學的大多數200行左右的Node.js源碼。今天推薦這篇文章。&#xff08;剛剛在寫明天掘金要發的文章&#xff0c;差點忘記今天還沒發文。在知乎上看…

shields 徽標_我的徽標素描過程

shields 徽標Sketching is arguably the most important part of my process when it comes to logo design. In the beginning of my design career, I would actually skip this step completely and go right to the computer. I’d find myself getting stuck and then goi…

VC編程心得

VC編程心得 開始&#xff1a; 聲明變量要初始化&#xff1b; 指針變量申請空間后是不是為空&#xff08;申請不成功&#xff09;&#xff1b; 過程&#xff1a; CREATE、OPEN了的東西賦給指針變量&#xff0c;要看指針變量是否為空&#xff1b; 指針變量在調用其方法之前&#…

叮咚,系統檢測到 npm 有更新,原理揭秘!

大家好&#xff0c;我是若川。最近組織了源碼共讀活動&#xff0c;感興趣的可以加我微信 ruochuan12本文來自V同學投稿的源碼共讀第六期筆記&#xff0c;寫得很有趣。現在已經進行到第十期了。你或許經常看見 npm 更新的提示。npm 更新提示面試官可能也會問你&#xff0c;組件庫…

ui設計未來十年前景_UI設計的10條誡命

ui設計未來十年前景重點 (Top highlight)The year is approximately 1,300 BC when Moses received the 10 UI design commandments from the almighty design gods. The list was comprised of best practices that only the most enlightened designers would be aware of.當…

w3ctech 2011 北京站(組圖)

門前的牌子大廳一推低價技術書籍會場嘉賓席人漸漸到齊準備工作w3c中國區負責人 安琪 第一個演講焦峰同學分享了瀏覽器兼容性的相關問題石川同學分享的是JQuery的相關內容攝影哥微博大屏幕&#xff0c;有亮點哦。。。MBP啊有木有&#xff5e;&#xff5e;&#xff5e;貘大現場提…

Linux設備驅動之IIO子系統——IIO框架及IIO數據結構

Linux設備驅動之IIO子系統——IIO框架及IIO數據結構由于需要對ADC進行驅動設計&#xff0c;因此學習了一下Linux驅動的IIO子系統。本文翻譯自《Linux Device Drivers Development 》--John Madieu&#xff0c;本人水平有限&#xff0c;若有錯誤請大家指出。 IIO Framework 工業…

瀏覽器中的 ESM

大家好&#xff0c;我是若川。最近組織了源碼共讀活動&#xff0c;感興趣的可以加我微信 ruochuan12早期的web應用非常簡單&#xff0c;可以直接加載js的形式去實現。隨著需求的越來越多&#xff0c;應用越做越大&#xff0c;需要模塊化去管理項目中的js、css、圖片等資源。這里…

理解面向連接和無連接協議之間的區別

理解面向連接和無連接協議之間的區別 網絡編程中最基本的概念就是面向連接&#xff08;connection-oriented&#xff09;和無連接&#xff08;connectionless&#xff09;協議。 面向連接和無連接指的都是協議。也就是說&#xff0c;這些術語指的并不是無理介質本身&#xff0c…

標記圖標_標記您的圖標

標記圖標Not labeling your icons is the same as assuming that we are all fluent in ancient hieroglyphics. Are you? Can you just walk up to Cleopatras needle and read it like you could read a childrens book? Even emojis, our modern hieroglyphics dont mean …

找出無序數組中最小的k個數(top k問題)

2019獨角獸企業重金招聘Python工程師標準>>> 給定一個無序的整型數組arr&#xff0c;找到其中最小的k個數 該題是互聯網面試中十分高頻的一道題&#xff0c;如果用普通的排序算法&#xff0c;排序之后自然可以得到最小的k個數&#xff0c;但時間復雜度高達O(NlogN)&…

你應該知道的 Node 基礎知識

大家好&#xff0c;我是若川。最近組織了源碼共讀活動&#xff0c;感興趣的可以加我微信 ruochuan12 參與&#xff0c;已進行兩個多月&#xff0c;大家一起交流學習&#xff0c;共同進步。源碼共讀學的多數是 Node.js &#xff0c;今天分享一篇 Node.js 基礎知識的文章。一. N…

C# 中數據緩存總結

在C#嘗試了5種方法進行數據緩存&#xff0c;具體如下&#xff1a;(如有遺漏&#xff0c;錯誤歡迎大家指正&#xff0c;歡迎提建議。)1&#xff1a;Session方法&#xff1a;此方法是針對于每個用戶來的&#xff0c;如果用戶量比較大&#xff0c;那么建議不要采用此方法&#xff…

react 引入 mobx @babel/core: 7.2.2

為什么80%的碼農都做不了架構師&#xff1f;>>> yarn add babel/plugin-proposal-class-propertiesyarn add babel/plugin-proposal-decorators"babel": {"plugins": [["babel/plugin-proposal-decorators", {"legacy": …

面試官問:怎么自動檢測你使用的組件庫有更新

大家好&#xff0c;我是若川。最近組織了源碼共讀活動&#xff0c;感興趣的可以加我微信 ruochuan12本文來自V同學投稿的源碼共讀第六期筆記&#xff0c;寫得很有趣。現在已經進行到第十期了。你或許經常看見 npm 更新的提示。npm 更新提示面試官可能也會問你&#xff0c;組件庫…

設計模式完整備忘錄

小言&#xff1a;這不是設計模式講解型博文&#xff0c;以下將設計模式的概述、類圖&#xff0c;代碼示例&#xff0c;總結分每篇博文單獨展示&#xff0c;現將其歸類&#xff0c;便于以后翻閱&#xff0c;設計模式也不是一兩個月學完了就能完全領悟&#xff0c;它只告訴我們幾…

使用Microsoft Web Application Stress Tool對web進行壓力測試

你的Web服務器和應用到底能夠支持多少并發用戶訪問&#xff1f;在出現大量并發請求的情況下&#xff0c;軟件會出現問題嗎&#xff1f;這些問題靠通常的測試手段是無法解答的。本文介紹 了Microsoft為這個目的而提供的免費工具WAS及其用法。另外&#xff0c;本文介紹了一種Web應…