<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-11-04 22:17

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

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

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

          什么是原子化 CSS?

          首先,讓我們?yōu)?原子化 CSS (Atomic CSS) 給出適當(dāng)?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,并且會(huì)以視覺效果進(jìn)行命名。

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

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

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

          同時(shí)有些 UI 庫(kù)也會(huì)附帶一些 CSS 工具類作為框架的補(bǔ)充,如 Bootstrap[7]Chakra UI[8]

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

          背景

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

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

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

          剖析原子化 CSS

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

          傳統(tǒng)方案

          制作原子化 CSS 的傳統(tǒng)方案其實(shí)就是提供所有你可能需要用到的 CSS 工具。例如,你可能會(huì)用預(yù)處理器(這里選用的是 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" 來(lái)設(shè)置邊距。但正如你所見,用這種方法的情況下,你不能使用除了 1 到 10 之外的邊距,而且,即使你只使用了其中一條 CSS 規(guī)則,但還是要為其余幾條規(guī)則的文件體積買單。如果之后你還想支持不同的 margin 方向,使用比如 mt 代表 margin-topmb 代表 margin-bottom 等,加上這 4 個(gè)方向以后,你的 CSS 大小會(huì)變成原來(lái)的 5 倍。如果再有使用到像 :hover:focus 這樣的偽類時(shí),體積還會(huì)得更變大。以此類推,每多加一個(gè)工具類,往往意味著你 CSS 文件的大小也會(huì)隨之增加。這也就是為什么傳統(tǒng)的 Tailwind 生成的 CSS 文件會(huì)有數(shù) MB 的大小。

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

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

          按需生成

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

          傳統(tǒng)的方式不僅會(huì)消耗不必要的資源(生成了但未使用),甚至有時(shí)更是無(wú)法滿足你的需求,因?yàn)榭倳?huì)有部分需求無(wú)法包含在內(nèi)。

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

          為了實(shí)現(xiàn)這一點(diǎn),Windi CSS 和 Tailwind JIT 都采用了預(yù)先掃描源代碼的方式。下面是一個(gè)簡(jiǎn)單示例:

          import?glob?from?'fast-glob'
          import?{?promises?as?fs?}?from?'fs'

          //?通常這個(gè)是可以配置的
          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)容傳遞給生成器并配對(duì)?class?的使用情況
          ??}
          }

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

          為了在開發(fā)期間提供 HMR,通常會(huì)啟動(dòng)一個(gè) 文件系統(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?事件
          })

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

          痛癢

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

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

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

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

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

          快速且直觀,最重要的是,它的確解決了你的需求。但是,如果什么都需要我自己手動(dòng)添加,那我們?yōu)槭裁催€需要使用 Tailwind ?

          如果你對(duì) Tailwind 了解深入一些,那你可能知道它可以進(jìn)行額外配置。所以你需要花 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
          ????}
          ??}
          }

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

          Windi CSS 對(duì)規(guī)則相對(duì)寬松一些,會(huì)盡可能地根據(jù)你使用的 class 提供相應(yīng)的實(shí)用工具類。在這種情況下,border-10 在 Windi 中可以做到開箱即用。但是,由于 Windi 需要與 Tailwind 兼容,它還必須使用與 Tailwind 完全相同的配置項(xiàng)。盡管數(shù)字推斷的問題得到了解決,但如果你想添加一些自定義的工具,這將是一場(chǎng)噩夢(mèng)。下面是一個(gè)來(lái)自 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é)果還要長(zhǎng)。并且難以閱讀和維護(hù),同時(shí),它破壞了按需應(yīng)變的能力。

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

          向你介紹 UnoCSS

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

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

          引擎

          UnoCSS 是一個(gè)引擎,而非一款框架,因?yàn)樗?strong style="color: rgb(149, 151, 21);">并未提供核心工具類,所有功能可以通過(guò)預(yù)設(shè)和內(nèi)聯(lián)配置提供。

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

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

          //?以下預(yù)設(shè)目前還不存在,
          //?歡迎大家踴躍貢獻(xiàn)!
          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

          ????????//?選擇其中一個(gè)...或多個(gè)!
          ??????]
          ????})
          ??]
          }

          讓我們來(lái)看看如何使它們成為可能:

          直觀且完全可定制

          UnoCSS 的主要目標(biāo)是直觀性和可定制性。它可以讓你在數(shù)十秒內(nèi),定義你自己的 CSS 工具。

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

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

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

          當(dāng)在用戶代碼庫(kù)中檢測(cè)到 m-1 時(shí),就會(huì)產(chǎn)生如下 CSS:

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

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

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

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

          例如,當(dāng)你使用:

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

          就會(huì)產(chǎn)生相應(yīng)的 CSS:

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

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

          可變修飾

          可變修飾 (Variants)[31] 在 UnoCSS 中也是簡(jiǎn)單且強(qiáng)大的。這里有幾個(gè)示例:

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

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

          預(yù)設(shè)

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

          同時(shí),我們?cè)诎l(fā)布時(shí)也提供了 一些預(yù)設(shè)[33] 供你快速上手。

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

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

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

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

          靈活性

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

          一起來(lái)見識(shí)下 UnoCSS 真正的威力:

          屬性化模式

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

          它會(huì)把你的冗長(zhǎng)的 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>

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

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

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

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

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

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

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

          純 CSS 圖標(biāo)

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


          <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)或顏色模式來(lái)切換圖標(biāo)。得益于 Iconify[41] 項(xiàng)目,你可以從一百余個(gè)熱門圖標(biāo)集合(Material Design Icons, Ant Design Icons 等等)中獲得 超過(guò)一萬(wàn)個(gè)圖標(biāo) 供你按需使用。

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

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

          CSS 作用域

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

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

          這也使得 UnoCSS 在 CSS 作用域上有了更多可能性。例如,我們?cè)?Vite 插件上有一個(gè)實(shí)驗(yàn)性的 scoped-vue 模式,可以為每個(gè)組件生成作用域樣式,你可以安全地使用原子化 CSS 作為組件庫(kù),而無(wú)需擔(dān)心與用戶的 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 代碼分割,以及模塊級(jí)別的 CSS 作用域等。

          性能

          考慮到 UnoCSS 帶來(lái)的靈活性和想象力,坦率地說(shuō),我認(rèn)為性能可能不是那么重要的事情。出于好奇,我寫了一個(gè) 簡(jiǎn)單的 benchmark[44] 來(lái)比較性能。結(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é)果來(lái)看,UnoCSS 可以比 Tailwind 的 JIT 引擎快 200 倍!說(shuō)實(shí)話,在按需生成的情況下,Windi 和 Tailwind JIT 都已經(jīng)算是超快了,UnoCSS 的性能提升感知度可能沒有那么高。然而,幾乎為零的開銷意味著你可以將 UnoCSS 整合到你現(xiàn)有的項(xiàng)目中,作為一個(gè)增量解決方案與其他框架一同協(xié)作,而不需要擔(dān)心性能損耗。

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

          跳過(guò)解析,不使用 AST

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

          單次迭代

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

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

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

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

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

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

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

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

          現(xiàn)在能用嗎?

          簡(jiǎn)而言之:可以,但有注意事項(xiàng)。

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

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

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

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

          關(guān)于 Windi CSS?

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

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

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

          結(jié)束語(yǔ)

          非常感謝你的閱讀!如果你對(duì)它感興趣,請(qǐng)記得查看 `antfu/unocss`[47] 倉(cāng)庫(kù)以了解更多細(xì)節(jié),也可以通過(guò) **在線 Playground**[48] 進(jìn)行嘗試。

          歡迎評(píng)論或轉(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]

          一個(gè) Vite 插件: https://github.com/windicss/vite-plugin-windicss

          [15]

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

          [16]

          自動(dòng)值推導(dǎo): 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 中進(jìn)行設(shè)計(jì): 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]

          自動(dòng)值推導(dǎo): 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]

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

          [34]

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

          [35]

          了解更多關(guān)于默認(rèn)預(yù)設(shè)的信息: https://github.com/antfu/unocss/tree/main/packages/preset-uno

          [36]

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

          [37]

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

          [38]

          一個(gè)提取器: 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]

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

          [44]

          簡(jiǎn)單的 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

          瀏覽 52
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  精品人妻无码系列 | 操逼逼AV | 天天爽夜夜躁夜夜爽 | 拉拉免费观看视频大全高清 | 亚洲国产成人AⅤ毛片大全密桃 |