【CSS】Tailwindcss 為何大受歡迎?

從 NPM.DEVTOOL[1] 的標簽中可以看出: 每個月 npm 下載量高達 300 萬次,jsDelivr 下載量更是高達 900 萬次,Star 數(shù)也即將突破 38K,依賴于它的 Packge 及 Github Repo 更是成千,足見其受歡迎程度。然而你需要使用它時,Node 的版本最好大于 12.13.0
本人的 CSS 方案常用 TailwindCSS,在這里談一點感受,并對一些常見的問題解答一下。TailwindCSS 因為一個 class 代表一個 CSS 屬性這種原子化 CSS (Atomic CSS),這種細粒度的 CSS 方案備受爭議。
這里把 CSS 樣式的方案分為四種粒度
一、四種粒度
<div style="{ borderRadius: '0.5rem', padding: '1rem' }"> Click </div>
<div class="rounded-lg p-4"> Click </div>
<div class="button"> Click </div>
<Button> Click </Button>
越往下走,顆粒度越來越大,約束性變高,自由性不足。而 TailwindCSS 位于第二層。
二、一些問題的解答
Tailwindcss 為啥受歡迎,在我看來無非是更好用的原子化的 CSS。在國外如火如荼,但是在國內(nèi)論壇上爭議很大,前幾天在我的博客上寫了一篇關(guān)于 Tailwindcss 的文章,但是底下評論很多了各種問題,于是再總結(jié)一下
Q1: 這和行內(nèi) CSS 有何區(qū)別,不就少寫幾個字嗎?
大部分人的想法應(yīng)該是這樣的: 僅僅對于 text-center 而言,雖然提供了些許方便,但是不足以拉開差距。
如果說它僅僅是簡單的原子化 CSS,好用卻不亮眼。但是它卻不僅僅止于此。
1. 方便性: text-center、grid-cols-3
或許一個 text-center 不足以使你覺得提供了多大的方便性,但對于一個三等分的 Grid 屬性來說,一個 grid-cols-3 和 shadow 絕對方便
.grid-cols-3 {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
或者你還能記得 box-shadow 各個位置的參數(shù)嗎? 我敢保證看這篇文章的人至少有 93% 說不上來
在 tailwind 中,直接用 .shadow 簡單方便
.shadow {
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
}
2. 語義化: text-lg、text-white、ring、animate-spin`
text-lg,一個較大字體,如果設(shè)置行內(nèi)樣式,肯定有諸多麻煩的事: 我想設(shè)計一個較大的字體,那我應(yīng)該設(shè)計多大尺寸、使用什么單位
同樣還有:
text-white: 白色的色值是哪個來著?ring: 我想給這個按鈕加一個圈圈?animate-spin: 怎么做一個動畫?
3. 約束性: bg-gray-500、text-lg、p-4
新人總?cè)菀着鲆环N大紅大綠的新人風(fēng)格網(wǎng)頁,有了一些約束就很難出現(xiàn)這種很糟糕的色彩控制
另外,有了 text-lg 此類,一個頁面上就不會出現(xiàn)幾十種參差不同的字體大小
4. 響應(yīng)式:
先來看一個在工作中會遇到的響應(yīng)式布局問題,這也是我上一次在頭條面試時的一道題目
響應(yīng)式布局,一大堆子元素,在大屏幕三等分,中等屏幕二等分,小屏幕一等分?[2]
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
通過 grid 布局很容易實現(xiàn),但未免繁瑣
@media (min-width: 1024px) {
.container {
grid-template-columns: repeat(3,minmax(0,1fr));
}
}
@media (min-width: 768px) {
.container {
grid-template-columns: repeat(2,minmax(0,1fr));
}
}
.conainer {
display: grid;
gap: 1rem;
}
那使用 tailwind 呢? 只要一行,就問你高效不高效
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"></div>
5. 修飾符
把修飾符,如各種偽類、暗黑模式、響應(yīng)式設(shè)計至于前綴的設(shè)計簡直深得我心
<div class="focus:ring-2 hover:bg-red-700 dark:bg-gray-800"></div>
Q2: 既然 TailwindCSS 這么好用,那豈不是可以擺脫手寫 CSS 了
很遺憾,不能。不過雖然你無法擺脫手寫 CSS,但是你基本上也寫不了幾行,說以下幾種很常用的情況
1. 復(fù)雜選擇器
當父級元素鼠標懸浮時的,子級元素的樣式控制
.container:hover .item {
}
2. CSS function
.body {
height: calc(100vh - 6rem)
}
3. 多種屬性復(fù)用
.item {
@apply p-2 border-b flex justify-between font-mono;
}
Q3: 造成新的記憶負擔
這個問題就仁者見仁智者見智了,在 Vue 的 template 語法中也經(jīng)常出現(xiàn)此類問題,很多人會對一些命名上的約定,特別是自己不太喜歡的約定天然排斥,這也無可厚非。
我在初期確實會一邊開發(fā)網(wǎng)頁,一邊瞅著文檔全局搜索: TailwindCSS 屬性查找[3],現(xiàn)在借助瀏覽器插件及熟能生巧的經(jīng)驗已經(jīng)不太需要翻文檔了
Tailwind CSS IntelliSense[4]


初期經(jīng)?;〞r間翻文檔而不手寫 CSS,而其中的原因不外乎兩個:
多寫幾個字母,確實有點嫌麻煩,有這時間還不如看看文檔,全局搜索下也不費事 自己設(shè)置一個 fontSize,padding、margin 實在不知道設(shè)置多少尺寸,tailwindcss 有較大的約束
過了多久就會覺得: 嗯,真香
Q4: 僅僅是實現(xiàn)一個原子化的 CSS,怎么這么多 Star?
因為他確實比較方便,算是解決了痛點。而且 Star 也和功能復(fù)雜度無關(guān)
使用 Gihub Star 數(shù)和 npm 周下載量來表明一個庫的受歡迎程度,那么你會發(fā)現(xiàn)有的庫只有幾十行,下載量比 React/Vue/Angular 加起來都要多
mime[5]: 獲取 Mime Type。幾十行代碼,每周 3700 萬次下載,1.6K Star classnames[6]: 根據(jù)條件連接 class 類名。一百幾十行代碼,每周 580 萬次下載,13.5K Star js-cookie[7]: 操作 cookie。一百多行代碼,每周 200 萬次下載,17K Star,但是在 Jsdelivr 每月有上億次下載 ms[8]: ms('2 days')可讀性的時間轉(zhuǎn)化為秒數(shù)。幾十行代碼,每周 7300 萬次下載,3.3K StarisMobile[9]: 檢測當前 web 環(huán)境是否為移動端。幾十行代碼,每周 15 萬下載,2K Star isci[10]

Q5: 初期很爽,但是后期維護困難,特別是人員調(diào)動后
我算是 TailwindCSS 中度使用者吧,目前還沒有后期維護困難方面的困惑。甚至可以說,維護比以前的方案還要容易
至于調(diào)試,可輕松使用 chrome devtools,還是可以一眼望到底的,而且沒有以前各種 class 存在屬性重復(fù)覆蓋,造成調(diào)試困難,從下圖可看出 tailwindcss 調(diào)試一目了然

即使實在有過多的 CSS Class,也可以通過 Computed 面板中的小箭頭跳轉(zhuǎn)過去找到相對應(yīng)的 class

Q6: 雖然 CSS 體積大幅降低,但是 HTML 體積卻變大了
Facebook 經(jīng)過重構(gòu)后 CSS 體積已經(jīng)從 413Kb 減至 74Kb。

gzip 的核心是 Deflate,而它使用了 LZ77 算法與 Huffman 編碼來壓縮文件,重復(fù)度越高的文件可壓縮的空間就越大。
即使 HTML 因為類名過多造成體積增大,由于 class 高度相似,gzip 也將會得到一個很大的壓縮比例。
三、談?wù)勎矣龅降膸讉€問題
Q1: PurgeCSS 有可能過多刪除 class
tailwind 使用了 purgecss 刪除無用的 class,但有時候會吧有用的 class 也給刪掉。道理也很簡單,它并不能動態(tài)執(zhí)行代碼,你計算后的 class 他不認識就給你刪了
比如:
<div class="text-{{ error ? 'red' : 'green' }}-600"></div>
所以我強烈推薦使用 classnames[11]
上面這段在文檔里有描述: 如何正確書寫能夠被 purgecss 識別的樣式[12]
Q2: 樣式覆蓋問題
以下 red 與 blue 兩個樣式哪個會生效?無法確定。
<div class="red blue"> </div>
class 在樣式表中的順序決定,而非在 class 中的先后順序。這使得通過 className 擴展樣式時遭遇問題,示例如下
import cx from 'classnames'
function Input ({ classname }) {
// 默認居中,提供外層擴展 class 的功能
return <input className={cx('text-center', classname)} />
}
function ExtendInput () {
// 擴展失敗
return <Input className="text-left" />
}
實踐
再來談幾個實踐的點
與 classnames 搭配使用
classNames('text-center transition-opacity', showTip ? 'opacity-100' : 'opacity-0')
與 styled-jsx 搭配使用
此時樣式由大量的 tailwind 及少量的 styled-jsx組成。需要注意此時需要搭配 styled-jsx 的 postcss 插件使用
<style jsx>{`
.item {
@apply p-2 border-b flex justify-between font-mono;
}
`}</style>
參考資料
NPM.DEVTOOL: https://npm.devtool.tech/tailwindcss
[2]響應(yīng)式布局,一大堆子元素,在大屏幕三等分,中等屏幕二等分,小屏幕一等分?: https://q.shanyue.tech/fe/css/473.html
[3]TailwindCSS 屬性查找: https://tailwindcss.com/docs/border-radius
[4]Tailwind CSS IntelliSense: https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss
[5]mime: https://npm.devtool.tech/mime
[6]classnames: https://npm.devtool.tech/classnames
[7]js-cookie: https://npm.devtool.tech/js-cookie
[8]ms: https://npm.devtool.tech/ms
[9]isMobile: https://npm.devtool.tech/is-mobile
[10]isci: https://npm.devtool.tech/is-ci
[11]classnames: https://npm.devtool.tech/classnames
[12]如何正確書寫能夠被 purgecss 識別的樣式: https://tailwindcss.com/docs/optimizing-for-production#writing-purgeable-html
