可擴(kuò)展 CSS 的演變

大家好,我是 CUGGZ。
自 Web 誕生以來(lái),我們編寫和思考 CSS 的方式發(fā)生了巨大變化。從基于 table 的布局到響應(yīng)式網(wǎng)頁(yè)設(shè)計(jì),已經(jīng)走過(guò)了漫長(zhǎng)的道路,現(xiàn)在進(jìn)入了由現(xiàn)代 CSS 功能提供支持的自適應(yīng)布局的新時(shí)代。而管理和組織 CSS 一直具有挑戰(zhàn)性,很難達(dá)成共識(shí)。

深入研究在大型項(xiàng)目上擴(kuò)展 CSS 的問(wèn)題,可以加深對(duì) CSS 的理解。我們將了解隨著時(shí)間的推移出現(xiàn)和改變的各種 CSS 最佳實(shí)踐的演變。最后,我們將很好地掌握過(guò)去在大型項(xiàng)目上擴(kuò)展 CSS 的方法,以及 Tailwind 和其他一系列流行工具如何以反直覺(jué)的方式解決這些問(wèn)題。

CSS 出現(xiàn)之前
一開始,Web 只有 HTML。我們將其全部大寫,并直接在 HTML 上使用屬性來(lái)設(shè)置頁(yè)面樣式:

<BODY>
  <P SIZE="8" COLOR="RED">LOUD NOISES</P>
</BODY>
這是一個(gè)黑暗的時(shí)期,除了可用的樣式數(shù)量有限之外,一個(gè)明顯的限制就是需要不斷地重復(fù)。當(dāng)時(shí)一個(gè)典型例子是舊的 Space Jam 網(wǎng)站。

現(xiàn)在有些人可能會(huì)想“這看起來(lái)像是傳遞到組件中的 props”。正如我們稍后將看到的那樣,事情往往會(huì)隨著創(chuàng)新周期的轉(zhuǎn)折而全面發(fā)展。

樣式表和關(guān)注點(diǎn)分離
CSS 的出現(xiàn)就可以消除所有這些重復(fù)。樣式表使我們能夠以聲明方式設(shè)置頁(yè)面樣式,用很少的代碼即可影響大量元素:

p {
  color: red;
}
現(xiàn)在可以分別考慮內(nèi)容的結(jié)構(gòu)及其視覺(jué)外觀和布局。將布局問(wèn)題從使用 table 的 HTML 轉(zhuǎn)移到了 CSS 中。

隨著時(shí)間的推移,開始在一個(gè)名為 CSS Zen Garden 的網(wǎng)站上收集示例。CSS Zen garden 成為展示人們?nèi)绾蝿?chuàng)造性地使用 CSS 的中心。人們可以以有趣和獨(dú)特的方式提交重新設(shè)計(jì)了相同 HTML 樣式的 CSS 文件。

這對(duì)于傳播將內(nèi)容與其樣式、可重新設(shè)置樣式的 HTML 以及將可主題化的“皮膚”應(yīng)用于核心框架的想法等方面非常有影響力。

尋找最佳實(shí)踐
我們開始構(gòu)建更復(fù)雜的站點(diǎn)和應(yīng)用時(shí),對(duì) CSS 提出了新的要求。在最佳實(shí)踐出現(xiàn)之前,任何新技術(shù)通常會(huì)在幾個(gè)周期采用不同的方法。

Less 和 Sass 等工具的出現(xiàn),擴(kuò)展了原生 CSS 的功能。為我們提供諸如變量和計(jì)算函數(shù)之類的功能,極大地改善了開發(fā)人員的體驗(yàn)。

隨著在這些樣式表上花費(fèi)更多的時(shí)間,我們想方設(shè)法組織所有這些規(guī)則和選擇器。

許多不同的擴(kuò)展 CSS 的模式就出現(xiàn)了。這些試圖在維護(hù)、性能和可讀性之間取得平衡,被稱為“CSS 架構(gòu)”。

在深入研究這些架構(gòu)之前,首先來(lái)了解為什么在大型項(xiàng)目中管理 CSS 會(huì)很快變得復(fù)雜。

為什么CSS在大型項(xiàng)目難以管理?
“大型”指的是多個(gè)方面的交集,包括人員、工具、流程和性能。

有效擴(kuò)展需要謹(jǐn)慎管理復(fù)雜性的增長(zhǎng)。因此,隨著系統(tǒng)的發(fā)展,它仍然是可理解的、可變的和高效的。添加新代碼的成本盡可能低,人們才有信心更改和刪除舊代碼。

級(jí)聯(lián)(CSS中的C)起源于 Web 早期。瀏覽器可以將默認(rèn)樣式應(yīng)用于這些新的電子文檔。然后,文檔作者可以提供自己的樣式,這些樣式可以被單個(gè)用戶的首選項(xiàng)覆蓋。然后文檔作者可以提供他們自己的樣式,這些樣式可以被個(gè)人用戶偏好覆蓋。

使 CSS 強(qiáng)大的相同屬性也使得在大型項(xiàng)目中難以實(shí)現(xiàn)這些擴(kuò)展屬性。特別是它的全局命名空間、級(jí)聯(lián)規(guī)則和選擇器特異性。

全局命名空間
如果謹(jǐn)慎使用,全局 CSS 命名空間會(huì)非常強(qiáng)大。但在大型項(xiàng)目中,它會(huì)帶來(lái)很多問(wèn)題。

當(dāng)一切都是全局的時(shí),任何事情都可能出乎意料地影響其他事情。要么是現(xiàn)在,要么是將來(lái)的某個(gè)時(shí)候,當(dāng)事情發(fā)生變化時(shí)。

這很快就會(huì)成為問(wèn)題。在其他語(yǔ)言中,沒(méi)有將所有內(nèi)容都放在全局命名空間中是有原因的。隨著更多代碼的添加,代碼會(huì)變得越來(lái)越難以預(yù)測(cè)和維護(hù)。

值得注意的是,CSS 的 Cascade Layers 是一項(xiàng)全新的功能,可以幫助解決這個(gè)問(wèn)題。

命名困難
使用 CSS 進(jìn)行項(xiàng)目快速迭代時(shí),創(chuàng)建一系列語(yǔ)義化類名通常會(huì)很煩瑣。當(dāng)我們?cè)噲D將一堆信息壓縮到一個(gè)精確的標(biāo)簽中時(shí),想出有用的名字就很困難。當(dāng)一切都是全局的時(shí)候,正確地處理這一點(diǎn)變得更加重要。

過(guò)早命名事物是過(guò)早抽象的一種形式。因?yàn)橥ǔN覀兠臇|西仍然需要完全形成,而且還不能重復(fù)使用。

在前端,更改設(shè)計(jì)是很常見(jiàn)的,這些標(biāo)簽經(jīng)常會(huì)過(guò)時(shí),需要對(duì)樣式和 HTML 進(jìn)行重構(gòu)。

重構(gòu)CSS困難
設(shè)計(jì)和現(xiàn)代軟件開發(fā)都是高度迭代的。這要求我們定期重新評(píng)估對(duì)正在解決的問(wèn)題的理解。在代碼中,這意味著隨著我們的理解發(fā)生變化和鞏固而進(jìn)行重構(gòu)。

重構(gòu)可能具有挑戰(zhàn)性,但它是一種久經(jīng)考驗(yàn)的真實(shí)方法,可以根據(jù)實(shí)際需求而不是理論來(lái)獲得更好的抽象。

在 CSS 中,重構(gòu)相當(dāng)困難。如果沒(méi)有可靠的視覺(jué)回歸測(cè)試,許多 CSS 錯(cuò)誤都是“無(wú)聲的”,很容易產(chǎn)生無(wú)法預(yù)料的錯(cuò)誤和副作用。這導(dǎo)致了幾個(gè)常見(jiàn)的場(chǎng)景:

1. 僅附加樣式表
項(xiàng)目開始時(shí)樣式表都是易于管理的。但是,新代碼在經(jīng)過(guò)幾次迭代和錯(cuò)誤修復(fù)后,通常會(huì)停留在文件的末尾。我們很難知道何時(shí)可以安全地更改或刪除規(guī)則,所以在文件末尾的級(jí)聯(lián)中覆蓋了之前的內(nèi)容。

這就是特異性之爭(zhēng)的原因,我們可能都有過(guò)需要覆蓋其他樣式的經(jīng)歷。!important 的出現(xiàn)加重了維護(hù)負(fù)擔(dān)。

2. 死代碼
在實(shí)踐中,我們經(jīng)常重復(fù)使用相同的 CSS 屬性。不斷復(fù)制規(guī)則通常比承擔(dān)在全局命名空間中重構(gòu)大量 CSS 的風(fēng)險(xiǎn)要安全得多。

這通常會(huì)導(dǎo)致大量未使用的 CSS,很難知道是否有東西依賴它。這最終會(huì)導(dǎo)致 CSS 散布在各種文件中。

調(diào)試CSS困難
調(diào)試的很大一部分就是在腦海中模擬計(jì)算機(jī)所做的事。調(diào)試復(fù)雜的 CSS 是很困難的,因?yàn)樵诳紤]代碼順序的同時(shí)要在心里計(jì)算級(jí)聯(lián)和計(jì)算的最終規(guī)則。

特別是 CSS 在定位、對(duì)齊、層疊上下文、邊距和高度方面的許多細(xì)微差別。如果沒(méi)有系統(tǒng)的方法,常見(jiàn)的 CSS 調(diào)試工作流程通常會(huì)涉及調(diào)整一些值以查看會(huì)發(fā)生什么。

當(dāng)處理無(wú)法控制的代碼或特定于瀏覽器的錯(cuò)誤時(shí),這尤其具有挑戰(zhàn)性。

用CSS架構(gòu)控制復(fù)雜性
CSS 有一個(gè)簡(jiǎn)單的模型,但事情很容易很快變得混亂。我們最終開始尋求應(yīng)用軟件工程原理來(lái)幫助我們進(jìn)行管理。這些架構(gòu)更像是組織 CSS 文件及其規(guī)則和選擇器的高級(jí)藍(lán)圖。下面來(lái)快速了解一些有影響力和流行的 CSS 架構(gòu)及其主要思想。

OOCSS:面向?qū)ο蟮?CSS
OOCSS 區(qū)分了我們?cè)趯?shí)踐中編寫的不同類型的 CSS。進(jìn)行頁(yè)面布局的 CSS,以及為 HTML 設(shè)置主題或“皮膚”的 CSS,如顏色、字體等。

OOCSS 中的“對(duì)象”是一種可以抽象和重用的重復(fù)視覺(jué)模式,其思想就是識(shí)別常見(jiàn)的視覺(jué)模式,并將重復(fù)的代碼塊提取到可重用類中。

使用最廣泛的 CSS 框架之一 Bootstrap 就使用了 OOCSS。

SMACSS:可擴(kuò)展和模塊化的 CSS
在實(shí)踐中,大型單文件 CSS 文件會(huì)很快變得難以管理且難以調(diào)試。SMCASS 是對(duì)不同類型的 CSS 進(jìn)行分類的指南,并且與 OOCSS 等方法兼容。其主要想法是獲取所有類名,并將它們組織到單獨(dú)的桶中,并為 CSS 文件提供一些結(jié)構(gòu)。除了一些關(guān)于命名類的約定之外。

BEM:塊、元素、修飾符
BEM 是一種模型,用于考慮如何將事物分解為組件、子元素以及各種離散狀態(tài)。

BEM 最初創(chuàng)建于 Yandex,它提供了一個(gè)系統(tǒng)的命名約定,通過(guò)保持所有選擇器扁平化(沒(méi)有后代選擇器)來(lái)避免特異性之爭(zhēng),其中每個(gè)被設(shè)置樣式的元素都有自己的類名。

BEM 與 Sass 等流行的 CSS 預(yù)處理器很好地融合在一起,嵌套規(guī)則可以編譯成扁平化的 CSS 選擇器:

.nav {
  // 塊樣式
 &__link {
    // 依賴父塊的元素樣式
  &--active {
        // 修飾符樣式
  }
 }
}
ITCSS:倒三角形
ITCSS 背后的主要思想之一是通過(guò)分層的角度來(lái)考慮樣式表,以幫助控制級(jí)聯(lián)。

ITCSS 類似于 CSS 的“元框架”,與其他方法兼容。它通過(guò)提供越來(lái)越具體的明確層次來(lái)控制一切不可預(yù)測(cè)地相互壓倒的混亂。

“倒三角形”來(lái)自每個(gè)漸進(jìn)層形成的倒金字塔形狀。這是管理大型項(xiàng)目 CSS 文件的一種有影響力的方法。

Cube CSS
Cube CSS 與全局命名空間以及級(jí)聯(lián)一起工作,它提供了一組定義明確的桶,用于對(duì) CSS 進(jìn)行分類。這些就構(gòu)成了 Cube 的首字母縮寫詞:Composition、Utility、Block、Exception。這是一種松散的方法論,就像組織 CSS 的心智模型。






與 ITCSS 類似,它是一個(gè)有影響力的“元 CSS 框架”,兼容各種方法。

重新思考關(guān)注點(diǎn)分離
隨著 SPA 和組件驅(qū)動(dòng)開發(fā)的興起,我們開始看到 CSS 的新方法。

在這個(gè)世界上,管理 CSS 變得更加困難,因?yàn)榻M件現(xiàn)在是異步加載的,無(wú)法保證源順序。一個(gè)常見(jiàn)的問(wèn)題是,當(dāng)執(zhí)行從頁(yè)面 A 到 B 的 SPA 轉(zhuǎn)換時(shí),頁(yè)面上的某些元素看起來(lái)有所不同,但如果直接加載到 B 則看起來(lái)很好。導(dǎo)致一些有趣的調(diào)試會(huì)話。

我們開始尋找更具體的解決方案來(lái)管理 CSS,這些解決方案與這種以組件為中心的新方法結(jié)合在一起來(lái)構(gòu)建我們的前端。

這些工具通常會(huì)打破我們迄今為止一直在建立和思考的許多既定的最佳實(shí)踐。下面來(lái)了解一下。

行內(nèi)樣式
基于組件的框架中通常會(huì)在組件內(nèi)部應(yīng)用內(nèi)聯(lián)樣式。在像 React 這樣的框架中,可以將一個(gè) JavaScript 對(duì)象傳遞給 style 屬性,就會(huì)將其轉(zhuǎn)換為內(nèi)聯(lián)樣式。

這會(huì)引起許多人的本能反應(yīng),因?yàn)檫@就像我們要回到?jīng)]有外部樣式表的起點(diǎn),拋棄了現(xiàn)有的最佳實(shí)踐。

在組件的上下文中,內(nèi)聯(lián)樣式不會(huì)面臨最初的大量重復(fù)問(wèn)題,因?yàn)樗环庋b在組件內(nèi)部。樣式只影響它們所在的元素,這是在組件中安全地添加和修改 CSS 的好方法。

內(nèi)聯(lián)樣式的主要問(wèn)題就是無(wú)法使用更強(qiáng)大的 CSS 功能,例如偽類選擇器和媒體查詢。

CSS in JS
在 React 早期,Veujx 就 Facebook 的 CSS 方法發(fā)表了演講。從表面上看,這看起來(lái)很像內(nèi)聯(lián)樣式,但可以訪問(wèn)樣式表的強(qiáng)大功能。這次演講使得采用 JavaScript 驅(qū)動(dòng)的 CSS 方法的開源庫(kù)激增。

第一波 CSS in JS 庫(kù)因?yàn)?Styled Components、Emotion 等庫(kù)而流行。它們解決了原生 CSS 在使用組件的大型項(xiàng)目中遇到的很多問(wèn)題,使得處理 JavaScript 中的動(dòng)態(tài)值變得非常容易。

CSS in JS 存在服務(wù)器端渲染效率低下、緩存問(wèn)題和客戶端運(yùn)行時(shí)成本問(wèn)題。這加劇了緩慢的應(yīng)用啟動(dòng)時(shí)間,一旦 JavaScript 水合就需要多次重新渲染。

最近的第二波 CSS in JS 庫(kù)旨在在不增加運(yùn)行時(shí)成本的情況下為開發(fā)人員提供最佳體驗(yàn)。Linaria、Compiled、Vanilla extract 等工具可以在編譯中從組件中提取樣式表。這將在用戶瀏覽器運(yùn)行時(shí)發(fā)生的大部分事情轉(zhuǎn)移到了編譯時(shí)。

CSS 通常被編譯成 Atomic CSS 以避免 CSS 文件臃腫,并且與動(dòng)態(tài)運(yùn)行時(shí)樣式表相比更容易緩存。

CSS Modules
CSS Modules 可以在編寫常規(guī) CSS(或 Sass)和滿足正在尋找的許多擴(kuò)展屬性之間取得平衡。

CSS Modules 允許我們使用 CSS 的全部功能,而不必?fù)?dān)心樣式在組件中溢出,同時(shí)將內(nèi)容保持在組件目錄中。

特別是在第一波 CSS in JS 庫(kù)的浪潮中,將 CSS 綁定到特定的庫(kù)對(duì)一些人來(lái)說(shuō)太過(guò)分了,CSS Modules 就是一個(gè)很好的選擇。然而,有些人可能認(rèn)為這是 CSS in JS 的一種形式,因?yàn)樗蕾囉谙?Webpack 這樣的打包工具來(lái)生成并確保選擇器的作用域。

無(wú)論如何,CSS Modules 是常規(guī) CSS 世界和完全以組件為中心的方法(如 CSS in JS)之間的一個(gè)很好的中間方法。不過(guò),仍然需要提供名稱并與 BEM 等約定兼容。

具有挑戰(zhàn)性的CSS最佳實(shí)踐
同時(shí),在基于組件的 SPA 世界之外,最初的 CSS Zen Garden 影響最佳實(shí)踐在另一個(gè)方面受到挑戰(zhàn)。

Atomic CSS 誕生于在大型項(xiàng)目上管理 CSS 的黑暗中。它最初的動(dòng)機(jī)就是啟用樣式而無(wú)需編輯或?qū)⒁?guī)則附加到現(xiàn)有得樣式表。避免隨之而來(lái)的所有問(wèn)題。

與 OOCSS、BEM 和 SMACSS 等其他 CSS 架構(gòu)相比,Atomic CSS 完全違反直覺(jué),它比“塊”和“對(duì)象”低一級(jí),專注于單一用途的原子。直接違背既定的最佳實(shí)踐,甚至在 HTML 規(guī)范中概述了如何不命名 CSS 類。

對(duì)于感覺(jué)修改現(xiàn)有 CSS 風(fēng)險(xiǎn)太大的項(xiàng)目團(tuán)隊(duì),它已成為一種流行的提高生產(chǎn)力的方法。一些使用 Atomic CSS 的流行 CSS 庫(kù)包括 ACSS、Tachyons、WindiCSS 等。

根據(jù) state of CSS,這種 CSS 架構(gòu)最流行的實(shí)現(xiàn)之一就是 Tailwind CSS 框架。

Tailwind 的崛起
自 2017 年發(fā)布以來(lái),Tailwind 迅速受到歡迎。Tailwind的一個(gè)典型證明是,它通過(guò)使CSS更容易被非專家使用而提高了生產(chǎn)力,同時(shí)也帶來(lái)了更易于維護(hù)的 CSS。

Tailwind 的基本原則
為了了解它為什么如此受歡迎,下面來(lái)研究一下 Tailwind 方法背后的基本原則。盡管看似拋棄了既定的最佳實(shí)踐,但下面將介紹一系列其在實(shí)踐中行之有效的實(shí)用原則。

減少命名
不必不斷地為事物命名是 Tailwind 感覺(jué)如此高效的原因之一。這一工作流程是由自下而上組成單一用途原子的想法支持的。從可維護(hù)性的角度來(lái)看,這是避免倉(cāng)促抽象的好方法。

從不命名任何東西會(huì)影響代碼的可讀性。通常會(huì)導(dǎo)致一大堆沒(méi)有明確界限的原子類(或組件)。不過(guò),在經(jīng)常更改或與許多人一起更改的代碼庫(kù)中,這通常是正確的權(quán)衡。

適時(shí)抽象
Tailwind 提供了兩種在適當(dāng)?shù)臅r(shí)候進(jìn)行抽象的技術(shù),一種是創(chuàng)建一個(gè)共享的 CSS 類來(lái)表示一個(gè)塊(類似于 OOCSS)?;蛘咴谑褂没诮M件的框架時(shí),更鼓勵(lì)的做法是將重復(fù)的類提取到可重用(React、Vue、Solid、Svelte 等)組件中并共享它。

有信心重構(gòu)
因?yàn)轭愂歉鶕?jù)它們所在的 HTML 進(jìn)行本地化的,所以可以放心地重構(gòu)這些類,而不必?fù)?dān)心影響其他元素或其他地方的組件。

這適用于作為文檔的 Web 心智模型和以組件為中心的模型。這導(dǎo)致了一種感覺(jué),即 Tailwind 可以根據(jù)正在構(gòu)建的站點(diǎn)或應(yīng)用的類型進(jìn)行擴(kuò)展。

避免死代碼
預(yù)編譯為 Atomic CSS 的 Tailwind 和 CSS in JS 解決了充滿重復(fù)規(guī)則的臃腫 CSS 文件的問(wèn)題。

使用 Atomic CSS,CSS 的增長(zhǎng)與使用的樣式數(shù)量相關(guān),而不是開發(fā)人員提供的功能數(shù)量。例如,到處重用某些屬性(如 flex)是很常見(jiàn)的。與其在不同類名下的樣式表中復(fù)制這些內(nèi)容,不如只寫一次。對(duì)于每個(gè)屬性/值的組合都是如此。

縮小設(shè)計(jì)差距
需要記住,CSS 最終是關(guān)于實(shí)現(xiàn)視覺(jué)設(shè)計(jì)的。許多開發(fā)人員覺(jué)得 CSS 很難的一個(gè)重要原因是設(shè)計(jì)很難。掌握正確的基礎(chǔ)知識(shí)會(huì)大有幫助。在視覺(jué)設(shè)計(jì)中,一些關(guān)鍵元素是對(duì)齊、間距、一致性、大小、排版和顏色。

在 CSS 中,對(duì)于任何給定的屬性,如 font-size、color 或者 padding,都有多種方法來(lái)實(shí)現(xiàn)值。通常會(huì)使用某一種方式來(lái)實(shí)現(xiàn),這導(dǎo)致字體大小、間距和顏色不一致的細(xì)微差別激增,最終形成了一種粗糙的外觀和感覺(jué)。

擴(kuò)展 CSS 的一個(gè)關(guān)鍵部分就是通過(guò)具有可共享原語(yǔ)的堅(jiān)實(shí)基礎(chǔ)來(lái)彌合這一設(shè)計(jì)差距,這些原語(yǔ)定義了間距、字體大小、顏色值。這些通常被稱為 Design Tokens,并構(gòu)成設(shè)計(jì)系統(tǒng)的基礎(chǔ)。沒(méi)有這些基礎(chǔ),事情會(huì)變得非常隨意和混亂。

Tailwind 受歡迎的一個(gè)關(guān)鍵方面是提供了一組可以開箱即用的預(yù)先考慮好的基礎(chǔ)設(shè)計(jì)原語(yǔ)。這消除了大量的決策,這些決策通常是臨時(shí)完成的,且不一致。

結(jié)語(yǔ)
沒(méi)有工具是完美的,每個(gè)項(xiàng)目和團(tuán)隊(duì)都是不同的。無(wú)論采用何種方法,建立縮小設(shè)計(jì)差距的基礎(chǔ)都是擴(kuò)展 CSS 的關(guān)鍵要素。

專注于在其之上組合和構(gòu)建的原語(yǔ)也有很長(zhǎng)的路要走。這也適用于使用組件庫(kù)的基于組件的大型應(yīng)用。提供可組合的組件布局原語(yǔ),如 Box、Stack、Inline 等是管理 CSS 的好方法,開發(fā)人員無(wú)需編寫任何 CSS。

最近,Evergeen 瀏覽器推出了大量功能,解決了許多使 CSS 難以擴(kuò)展的痛點(diǎn)。級(jí)聯(lián)層、容器查詢、子網(wǎng)格、has 等新功能可能會(huì)改變我們?cè)谖磥?lái)思考和利用 CSS 的方式。

擴(kuò)展CSS的成功與其說(shuō)是對(duì)特定原則或最佳實(shí)踐的教條式堅(jiān)持,不如說(shuō)是基于現(xiàn)實(shí)世界的約束來(lái)定義你需要什么,并以可持續(xù)和高效的方式來(lái)完成工作。

原文:https://frontendmastery.com/posts/the-evolution-of-scalable-css/

作者:CUGGZ


歡迎關(guān)注微信公眾號(hào) :前端充電寶