<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          重新構(gòu)想原子化 CSS

          共 14076字,需瀏覽 29分鐘

           ·

          2021-10-29 19:57

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

          本文會比往期文章相對長些。這是我個人的一個重大的工具發(fā)布,有許多內(nèi)容我想談?wù)摗H绻隳芑ㄐr間讀完,不勝感激,希望能對你有所幫助 :)

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

          什么是原子化 CSS?

          首先,讓我們?yōu)?原子化 CSS (Atomic CSS) 給出適當?shù)亩x:

          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 的架構(gòu)方式,它傾向于小巧且用途單一的 class,并且會以視覺效果進行命名。

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

          .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 的優(yōu)缺點,相信你己經(jīng)在各種地方聽到了許多討論。今天我們將從框架作者的角度來聊聊如何權(quán)衡設(shè)計以構(gòu)建出那些你喜歡的框架,它們的局限性,以及如何能將它們做得更好以使得我們的日常工作受惠。

          背景

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

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

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

          剖析原子化 CSS

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

          傳統(tǒng)方案

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

          // style.scss

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

          編譯結(jié)果為:

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

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

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

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

          按需生成

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

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

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

          為了實現(xiàn)這一點,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')
          ????//?將文件內(nèi)容傳遞給生成器并配對?class?的使用情況
          ??}
          }

          await?scan()
          //?掃描會在構(gòu)建/服務(wù)器啟動前完成
          await?buildOrStartDevServer()

          為了在開發(fā)期間提供 HMR,通常會啟動一個 文件系統(tǒng)監(jiān)聽器[24]

          import?chokidar?from?'chokidar'

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

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

          痛癢

          我現(xiàn)在在我?guī)缀跛械膽弥卸荚谑褂?Windi CSS,而且效果顯著,性能優(yōu)異,HMR 瞬間完成幾乎無法察覺。自動值推導[26]屬性化模式[27] 更是提升了我的開發(fā)體驗。到這里,我本該可以睡上一個好覺去想想其他事情了,但是有時候,它還是會瘙你癢癢打擾你的美夢。

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

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

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

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

          快速且直觀,最重要的是,它的確解決了你的需求。但是,如果什么都需要我自己手動添加,那我們?yōu)槭裁催€需要使用 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
          ????}
          ??}
          }

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

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

          將產(chǎn)生如下代碼:

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

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

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

          向你介紹 UnoCSS

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

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

          引擎

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

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

          import?UnocssPlugin?from?'@unocss/vite'

          //?以下預設(shè)目前還不存在,
          //?歡迎大家踴躍貢獻!
          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 的主要目標是直觀性和可定制性。它可以讓你在數(shù)十秒內(nèi),定義你自己的 CSS 工具。

          靜態(tài)規(guī)則

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

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

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

          .m-1?{?margin:?0.25rem;?}
          動態(tài)規(guī)則

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

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

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

          例如,當你使用:

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

          就會產(chǎn)生相應的 CSS:

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

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

          可變修飾

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

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

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

          預設(shè)

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

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

          值得一提的是,默認的 `@unocss/preset-uno`[34] 預設(shè)(實驗階段)是一系列流行的原子化框架的 通用超集,包括了 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;?}

          了解更多關(guān)于默認預設(shè)的信息[35]

          靈活性

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

          一起來見識下 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>

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

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

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

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

          現(xiàn)在變?yōu)椋?/p>

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

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

          純 CSS 圖標

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


          <div?class="i-ph-anchor-simple-thin"?/>

          <div?class="i-mdi-alarm?text-orange-400?hover:text-teal-400"?/>

          <div?class="i-logos-vue?text-3xl"?/>

          <button?class="i-carbon-sun?dark:i-carbon-moon"?/>

          <div?class="i-twemoji-grinning-face-with-smiling-eyes?hover:i-twemoji-face-with-tears-of-joy"?/>

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

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

          希望這些預設(shè)可以讓你對 UnoCSS 的靈活性有一個大致的了解。它還處于一個非常早期的階段,有很多可能性等待我們?nèi)ヌ剿鳌?/p>

          CSS 作用域

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

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

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

          <template>
          ??<div?class="m-2?rounded"><slot>div>
          <template>


          <style?scoped>
          .m-2{margin:0.5rem;}
          .rounded{border-radius:0.25rem;}
          style>

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

          性能

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

          10/21/2021,?2:17:45?PM
          1656?utilities?|?x50?runs

          none????????????????????????????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)

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

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

          跳過解析,不使用 AST

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

          單次迭代

          正如前文所述,Windi CSS 和 Tailwind JIT 都依賴于對文件系統(tǒng)的預掃描,并使用文件系統(tǒng)監(jiān)聽器來實現(xiàn) HMR。文件 I/O 不可避免地會引入開銷,而你的構(gòu)建工具實際上需要再次加載它們。那么我們?yōu)槭裁床恢苯永靡呀?jīng)被工具讀取過的內(nèi)容呢?

          除了獨立的生成器核心以外,UnoCSS 有意只提供了 Vite 插件(以后可能考慮其他的集成),這使得它能夠?qū)W⒂谂c Vite 的最佳集成。

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

          export?default?{
          ??plugins:?[
          ????{
          ??????name:?'unocss',
          ??????transform(code,?id)?{
          ????????//?過濾掉無需掃描的文件
          ????????if?(!filter(id))?return

          ????????//?掃描代碼(同時也可以處理開發(fā)中的無效內(nèi)容)
          ????????scan(code,?id)

          ????????//?我們只需要內(nèi)容,所以不需要對代碼進行轉(zhuǎn)換
          ????????return?null
          ??????},
          ??????resolveId(id)?{
          ????????return?id?===?VIRTUAL_CSS_ID???id?:?null
          ??????},
          ??????async?load(id)?{
          ????????//?生成的?css?會作為一個虛擬模塊供后續(xù)使用
          ????????if?(id?===?VIRTUAL_CSS_ID)?{
          ??????????return?{?code:?await?generate()?}
          ????????}
          ??????}
          ????}
          ??]
          }

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

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

          現(xiàn)在能用嗎?

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

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

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

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

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

          關(guān)于 Windi CSS?

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

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

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

          結(jié)束語

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

          歡迎評論或轉(zhuǎn)發(fā) 此推文[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 中進行設(shè)計: 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]

          文件系統(tǒng)監(jiān)聽器: 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]

          一些預設(shè): https://github.com/antfu/unocss#presets

          [34]

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

          [35]

          了解更多關(guān)于默認預設(shè)的信息: 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]

          你正在閱讀的網(wǎng)站: 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

          瀏覽 84
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  xxxx.亚洲成人 | 三级理论视频 | 韩国精品一二三 | 天天搡天天狠天干天啪 | 999热视频 |