可擴展 CSS 的演變

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

全局命名空間
如果謹慎使用,全局 CSS 命名空間會非常強大。但在大型項目中,它會帶來很多問題。

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

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

值得注意的是,CSS 的 Cascade Layers 是一項全新的功能,可以幫助解決這個問題。

命名困難
使用 CSS 進行項目快速迭代時,創(chuàng)建一系列語義化類名通常會很煩瑣。當我們試圖將一堆信息壓縮到一個精確的標簽中時,想出有用的名字就很困難。當一切都是全局的時候,正確地處理這一點變得更加重要。

過早命名事物是過早抽象的一種形式。因為通常我們命名的東西仍然需要完全形成,而且還不能重復使用。

在前端,更改設(shè)計是很常見的,這些標簽經(jīng)常會過時,需要對樣式和 HTML 進行重構(gòu)。

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

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

在 CSS 中,重構(gòu)相當困難。如果沒有可靠的視覺回歸測試,許多 CSS 錯誤都是“無聲的”,很容易產(chǎn)生無法預料的錯誤和副作用。這導致了幾個常見的場景:

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

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

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

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

調(diào)試CSS困難
調(diào)試的很大一部分就是在腦海中模擬計算機所做的事。調(diào)試復雜的 CSS 是很困難的,因為在考慮代碼順序的同時要在心里計算級聯(lián)和計算的最終規(guī)則。

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

當處理無法控制的代碼或特定于瀏覽器的錯誤時,這尤其具有挑戰(zhàn)性。

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

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

OOCSS 中的“對象”是一種可以抽象和重用的重復視覺模式,其思想就是識別常見的視覺模式,并將重復的代碼塊提取到可重用類中。

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

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

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

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

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

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

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

“倒三角形”來自每個漸進層形成的倒金字塔形狀。這是管理大型項目 CSS 文件的一種有影響力的方法。

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






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

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

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

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

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

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

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

在組件的上下文中,內(nèi)聯(lián)樣式不會面臨最初的大量重復問題,因為它被封裝在組件內(nèi)部。樣式只影響它們所在的元素,這是在組件中安全地添加和修改 CSS 的好方法。

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

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

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

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

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

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

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

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

特別是在第一波 CSS in JS 庫的浪潮中,將 CSS 綁定到特定的庫對一些人來說太過分了,CSS Modules 就是一個很好的選擇。然而,有些人可能認為這是 CSS in JS 的一種形式,因為它依賴于像 Webpack 這樣的打包工具來生成并確保選擇器的作用域。

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

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

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

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

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

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

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

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

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

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

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

有信心重構(gòu)
因為類是根據(jù)它們所在的 HTML 進行本地化的,所以可以放心地重構(gòu)這些類,而不必擔心影響其他元素或其他地方的組件。

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

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

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

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

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

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

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

結(jié)語
沒有工具是完美的,每個項目和團隊都是不同的。無論采用何種方法,建立縮小設(shè)計差距的基礎(chǔ)都是擴展 CSS 的關(guān)鍵要素。

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

最近,Evergeen 瀏覽器推出了大量功能,解決了許多使 CSS 難以擴展的痛點。級聯(lián)層、容器查詢、子網(wǎng)格、has 等新功能可能會改變我們在未來思考和利用 CSS 的方式。

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

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

作者:CUGGZ


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