CSS 選擇器命名是一個哲學問題。--- 張鑫旭 《CSS 選擇器世界》css
首先,須要你們思考一下這個問題。vue
。。。 一分鐘後。react
在我看來命名的究極目的,只有一個樣式複用。你品,你細品。git
一個項目一般不是隻有咱們一我的在開發,而且咱們也會引入一些第三方的庫。因此命名衝突是一個很是容易發生的事情。解決命名衝突的方案我知道有三種:github
一般咱們寫一些但願被別人複用的第三方組件的時候會採用,私有做用域前綴的方案。好比:ant-design .ant-*
, material-ui 的 .Mui*
。編輯器
然而這並不能 100% 的解決命名衝突問題。好比你的團隊以前約定了 .ant-
做爲通用前綴,此時又須要引入 and.design 的組件。又或者你同時引用的兩個不一樣的第三方庫,採用了相同的前綴。此時就會顯得比較的尷尬了。固然這個機率其實也不大啦。工具
私有做用域前綴,還有一種方法就是約定一些前綴來約束不一樣選擇器的功能。好比網頁NEC 裏面 :佈局(grid)(.g-);模塊(module)(.m-);元件(unit)(.u-)... 若是團隊裏面你們都承認這個那麼這甚是極好的。可是可能須要思考的點就在於。若是這些約定的過多或者過於複雜的時候,團隊其它成員是否可以接受或者達成一致。佈局
CSS-module 是一個我認爲很是讚的發明。它妙就妙在,我能夠在個人組件裏面用 .header
, 你也能夠在你的組件裏面用 .header
。可是咱們卻不用徹底不用擔憂命名衝突的問題。ui
一個優秀的解決方案就應該是這樣:在不改變我自身習慣的狀況下,還幫我解決個人痛點。spa
固然它仍是有它的代價的,原來咱們寫代碼:
import "../styles/global.css";
import "./header1.css";
import "./header2.css";
<h2 className="header1">標題1</h2>
<h2 className="header2 float-left">標題2</h2>
複製代碼
引入 CSS-module 寫代碼:
import "../styles/global.css";
import _header1 from "./header1.module.css";
import _header2 from "./header2.module.css";
<h2 className={_header1.header}>標題1</h2>
<h2 className={`${_header2.header} float-left`}>標題2</h2>
複製代碼
首先咱們須要用構建工具去構建 CSS-module(特別是在非 react 和 vue 構建的項目中)。
在使用的時候,咱們須要還須要給咱們的 CSS 文件,取一個額外的做用域名稱,告訴構建工具這個選擇器是來自哪兒(這個做用域名稱自己也會命名衝突的問題)。
更尷尬的是,若是咱們全局有一些通用樣式和 CSS-module 混用的時候,還得借用字符串模版的能力去拼接 className
,這很容易致使咱們的代碼編輯器的對於 CSS 選擇器的提示失效(不知道小夥伴們有什麼方案能夠解決這個問題)。
在我以前的文章當中,我屢次提到不用命名就是最好的命名。連名字都沒有就不用糾結命名的問題。對於不用命名的方案我知道也是有三種方案:
直接用別人取好的名字。
目前可能最流行的就是 tailwind 這應該是 github 上 star 數最多的 CSS 項目了。這個方案其實不是不用命名,而是預製好了能涵蓋咱們大多數業務場景的一些 CSS 類。因而咱們就基本上能夠是不多,或者是不用從新去給 CSS 命名了。
可是這麼多預製的規則上手是一個成本,而且也一樣有須要說服同項目的小夥伴使用的問題。
定義 style 規則,每一個人經過這個命名規則只能產生相同的名稱
Atomic css 是一種比 tailwind 原子化更加極致的方案。
Atomic css 並無預製 CSS hooks,它更像是從新定義了一套 style-inline 的編寫方式。由於沒有 CSS-hooks 因此也就不存在命名衝突的問題。
老實說,Atomic css 輸就輸在它的那套晦澀難懂的命名規則。可是你也不得不認可,若是整站都是用這個去編寫 style,確實能夠 100% 的解決命名的問題。而且你整站的 CSS 代碼至少能夠減小 50% 以上。
直接寫 style
當咱們進入到 js 盛行的時代,css-in-js 可讓咱們在不須要理解 Atomic CSS 那套命名規則的狀況下,而直接使用咱們最熟悉的 style 的方式,100% 的解決 CSS 命名的問題。
這裏說 100% 的解決 CSS 命名是不許確的。更確切的說,咱們是直接把樣式附着在了組件上,在複用組件的時候達到了樣式複用的目的。而非 CSS-in-js 的方式,是咱們在擁有組件命名的同時還要在組件內部爲樣式再建立一套命名規則。
尷尬的是,若是咱們有不少樣式須要複用的時候,咱們就須要建立不少的樣式組件。但是當咱們一旦開始建立樣式組件的時候,這又回到了咱們命名衝突的問題。由於組件的名稱也會容易衝突。只是相對來講,JS 能有比 CSS 更好的解決命名衝突的能力更好一些。
什麼是好的命名?在我看來就是大多數人都能很天然理解的名字就是比較的好的名字。
.btn-s
: 小按鈕.btn-m
: 中號按鈕.btn-l
: 大按鈕.btn-xl
: 超大按鈕好比咱們借用生活中表示衣物 size 的縮寫 s,m,l,xl 來表示一些元素的大小就是一個比較不錯的方案。既能保持可讀性,又精簡了咱們 CSS 名稱的長度。
.cs-radio
: radio 元素.cs-menu
: menu 元素.active
: 元素能夠用狀態.disabeld
: 元素不可用狀態再好比張鑫旭老師 《CSS 選擇器世界》中提到的 「從 HTML 標籤中尋找靈感」 我也是很是贊同的。
借用大多數人瞭解或者熟知的東西去命名,顯然比按照咱們本身的習慣去建立名稱要好得多。好比我喜歡跳舞,我用舞蹈裏面的專有名字去做爲我 CSS 的名稱,這顯然是要被其它同事懟死的。
命名規範的意義在於,經過一些約定,減小團隊成員的命名決策時間,以及統一團隊的命名風格。在咱們的團隊,有兩條比較有意思的規則這裏分享給你們:
_
(下滑線) 開頭的類名不能直接建立樣式;_
帶下滑線的類名錶示全局樣式;/* ❌ */
._disabled{ background-color:#f5f5f5; }
/* ✅ */
.btn._disabled{ background-color:#f5f5f5; }
/* ❌ */
.header ._disabled{ background-color:#f5f5f5; }
/* ✅ */
.header._disabled .btn{ background-color:#f5f5f5; }
複製代碼
一般須要採用這種方式建立的類名都是表示某些狀態的類名。這樣作的好處在於,咱們就有了不少不用擔憂命名衝突的公用類名。
.select._disabled{ /* */ }
.input._disabled{ /* */ }
.dialog._open{ /* */ }
.modal._open{ /* */ }
複製代碼
這樣咱們均可以放心的維護本身組件的狀態了,而不用擔憂個人 *._disabled
會影響到你的 *._dsiabled
。固然維護狀態更好的方式應該是:
.select:disabled{ /* */ }
.input:disabled{ /* */ }
.dialog[open]{ /* */ }
.modal[open]{ /* */ }
複製代碼
_
帶下滑線的類名錶示全局樣式
對於這條規則,緣由在於咱們想要有簡單的方式來區分全局樣式和局部樣式。後面咱們想到了用_
和 -
來區分這兩個狀態。
/* 全局header */
.g_header{}
/* 局部header */
.news-header{}
複製代碼
照理來講咱們用.g_
來表示全局樣式可讀性會更強。可是後面發現這樣的話就會發生這樣的狀況:
/* 全局加號圖標 */
.g_i_add{ }
/* 局部加號圖標 */
.i-add{ }
複製代碼
這明顯沒有直接用
/* 全局加號圖標 */
.i_add{ }
/* 局部加號圖標 */
.i-add{ }
複製代碼
這樣的方式來的簡單幹淨,而且這樣還能拓展出其它的全局做用域。因此索性咱們就直接利用這個符號自己來區分全局和局部的樣式。
對於命名來講,每每難的不是命名自己,而是在於你這個命名是否能被團隊中大多數人接受和認知。你有可能發現或者發明了一種,各方面都看起來天衣無縫的命名方案,但是團隊裏面的其它小夥伴就是不買帳,那也是徒勞無功。
重點仍是要看本身團隊的痛點是什麼。不論是什麼樣的命名,只要團隊裏面你們都能理解的就算是不錯的命名了。