快速掌握 Tailwind:最流行的原子化 CSS 框架

Tailwind 是流行的原子化 css 框架。

有多流行呢?

它現在有 68k star 了,要知道 express 才 60k:

那什麼是原子化 css?

我們平時寫 css 是這樣的:

<div class="aaa"></div>
.aaa {
    font-size: 16px;
    border: 1px solid #000;
    padding: 4px;
}

在 html 裏指定 class,然後在 css 裏定義這個 class 的樣式。

也就是 class 裏包含多個樣式:

而原子化 css 是這樣的寫法:

<div class="text-base p-1 border border-black border-solid"></div>
.text-base {
    font-size: 16px;
}
.p-1 {
    padding: 4px;
}
.border {
    border-width: 1px;
}
.border-black {
    border-color: black;
}
.border-solid {
    border-style: solid;
}

定義一些細粒度的 class,叫做原子 class,然後在 html 裏直接引入這些原子化的 class。

這個原子化 css 的概念還是很好理解的,但它到底有啥好處呢? 它解決了什麼問題?

口說無憑,我們試下 tailwind 就知道了,它就是一個提供了很多原子 class 的 css 框架。

我們通過 crerate-react-app 創建一個 react 項目:

npx create-react-app tailwind-test

然後進入 tailwind-test 目錄,執行

npm install -D tailwindcss
npx tailwindcss init

安裝 tailwindcss 依賴,創建 tailwindcss 配置文件。

tailwind 實際上是一個 postcss 插件,因爲 cra 內部已經做了 postcss 集成 tailwind 插件的配置,這一步就不用做了:

然後在入口 css 里加上這三行代碼:

這三行分別是引入 tailwind 的基礎樣式、組件樣式、工具樣式的。

之後就可以在組件裏用 tailwind 提供的 class 了:

import './App.css';

function App() {
  return (
    <div className='text-base p-1 border border-black border-solid'>guang</div>
  );
}

export default App;

我們執行 npm run start 把開發服務跑起來。

可以看到,它正確的加上了樣式:

用到的這些原子 class 就是 tailwind 提供的:

這裏的 p-1 是 padding:0.25rem,你也可以在配置文件裏修改它的值:

在 tailwind.config.js 的 theme.extend 修改 p-1 的值,設置爲 30px。

刷新頁面,就可以看到 p-1 的樣式變了:

.text-base 是 font-size、line-height 兩個樣式,這種通過數組配置:

也就是說所有 tailwind 提供的所有內置原子 class 都可以配置。

但這些都是全局的更改,有的時候你想臨時設置一些值,可以用 [] 語法。

比如 text-[14px],它就會生成 font-size:14px 的樣式:

比如 aspect-[4/3],就是這樣的樣式:

我們平時經常指定 hover 時的樣式,在 tailwind 裏怎麼指定呢?

很簡單,這樣寫:

生成的就是帶狀態的 class:

此外,寫響應式的頁面的時候,我們要指定什麼寬度的時候用什麼樣式,這個用 tailwind 怎麼寫呢?

也是一樣的寫法:

生成的是這樣的代碼:

這個斷點位置自然也是可以配置的:

可以看到 md 斷點對應的寬度變了:

光這些就很方便了。

之前要這麼寫:

<div class="aaa"></div>
.aaa {
    background: red;
    font-size: 16px;
}

.aaa:hover {
    font-size: 30px;
}

@media(min-width:768px) {
    .aaa {
        background: blue;
    }
}

現在只需要這樣:

<div class="text-[14px] bg-red-500 hover:text-[30px] md:bg-blue-500"></div>

省去了很多樣板代碼,還省掉了 class 的命名。

並且這些 class 都可以通過配置來統一修改。

感受到原子化 css 的好處了麼?

tailwind 文檔提到了 3 個好處:

不用起 class 名字,這點簡直太爽了,我就經常被起 class 名字折磨。

css 不會一直增長,因爲如果你用之前的寫法可能是這樣的:

多個 class 裏都包含了類似的樣式,但你需要寫多次,而如果用了原子 class,就只需要定義一次就好了。

css 沒有模塊作用域,所以可能你在這裏加了一個樣式,結果別的地方樣式錯亂了。

而用原子 class 就沒這種問題,因爲樣式是隻是作用在某個 html 標籤的。

我覺得光這三點好處就能夠說服我用它了,特別是不用起 class 名字這點。

當然,社區也有一些反對的聲音,我們來看看他們是怎麼說的:

一堆 class,可讀性、可維護性太差了

真的麼?

這種把 css 寫在 html 裏的方式應該是更高效纔對。

想想爲啥 vue 要創造個單文件組件的語法,把 js、css、template 放在一個文件裏寫,不就是爲了緊湊麼?

之前你要在 css、js 文件裏反覆跳來跳去的,查找某個 class 的樣式是啥,現在不用這麼跳了,直接在 html 裏寫原子樣式,它不香麼?

而且 tailwindcss 就前面提到的那麼幾個語法,沒啥學習成本,很容易看懂纔對。

但是還要每次去查文檔哪些 class 對應什麼樣式呀

這個可以用 tailwind css 提供的 vscode 插件來解決:

安裝這個 Tailwind CSS IntelliSense 之後的體驗是這樣的:

有智能提示,可以查看它對應的樣式。

不需要記。

難以調試

在 chrome devtools 裏可以直接看到有啥樣式,而且樣式之間基本沒有交叉,很容易調試:

相反,我倒是覺得之前那種寫法容易多個 class 的樣式相互覆蓋,還要確定優先級和順序,那個更難調試纔對:

類型太長了而且重複多次

這種問題可以用 @layer @apply 指令來擴展:

前面講過 @tailwind 是引入不同的樣式的,而 @layer 就是在某一層樣式做修改和擴充,裏面可以用 @apply 應用其他樣式。

效果是這樣的:

內置 class 不能滿足我的需求

其實上面那個 @layer 和 @apply 就能擴展內置原子 class。

但如果你想跨項目複用,那可以開發個 tailwind 插件

const plugin = require('tailwindcss/plugin');

module.exports = plugin(function({ addUtilities }) {
    addUtilities({
        '.guang'{
            background: 'blue',
            color: 'yellow'
        },
        '.guangguang'{
            'font-size''70px'
        }
    })
})

在 tailwind.config.js 裏引入:

這樣就可以用這個新加的原子 class 了:

插件的方式或者 @layer 的方式都可以擴展。

tailwind 的 class 名和我已有的 class 衝突了咋辦?

比如我本來有個 border 的 class:

而 tailwind 也有,不就衝突了麼?

這個可以通過加 prefix 解決:

不過這樣所有的原子 class 都得加 prefix 了:

知道了什麼是原子 css 以及 tailwind 的用法之後,我們再來看看它的實現原理。

tailwind 可以單獨跑,也可以作爲 postcss 插件來跑。這是因爲如果單獨跑的話,它也會跑起 postcss,然後應用 tailwind 的插件:

所以說,tailwind 本質上就是個 postcss 插件

postcss 是一個 css 編譯器,它是 parse、transform、generate 的流程。

在 astexplorer.net 可以看到 postcss 的 AST:

而 postcss 就是通過 AST 來拿到 @tailwind、@layer、@apply 這些它擴展的指令,分別作相應的處理,也就是對 AST 的增刪改查。

那它是怎麼掃描到 js、html 中的 className 的呢?

這是因爲它有個 extractor 的東西,用來通過正則匹配文本中的 class,之後添加到 AST 中,最終生成代碼。

extractor 的功能看下測試用例就明白了:

所以說,tailwind 就是基於 postcss 的 AST 實現的 css 代碼生成工具,並且做了通過 extractor 提取 js、html 中 class 的功能。

tailwind 還有種叫 JIT 的編譯方式,這個原理也容易理解,本來是全部引入原子 css 然後過濾掉沒有用到的,而 JIT 的話就是根據提取到的 class 來動態引入原子 css,更高效一點。

最後,爲啥這個 css 框架叫 tailwind 呢?

因爲作者喜歡叫做 kiteboarding 風箏衝浪的運動。

就是這樣的,一個風箏,一個衝浪板:

這種運動在順風 tailwind 和逆風 headwind 下有不同的技巧。而 tailwind 的時候明顯更加省力。

所以就給這個 css 框架起名叫 tailwind 了。

確實,我也覺得用這種方式來寫 css 更加省力、高效,不用寫 class 名字了,代碼更簡潔了,還不容易樣式衝突了。

總結

tailwind 是一個流行的原子化 css 框架。

傳統 css 寫法是定義 class,然後在 class 內部寫樣式,而原子化 css 是預定義一些細粒度 class,通過組合 class 的方式完成樣式編寫。

tailwind 用起來很簡單:

所有預定義的 class 都可以通過配置文件修改值,也可以通過 aaa-[14px] 的方式定義任意值的 class。

所有 class 都可以通過 hover:xxx、md:xxx 的方式來添加某個狀態下的樣式,響應式的樣式,相比傳統的寫法簡潔太多了。

它的優點有很多,我個人最喜歡的就是不用起 class 的名字了,而且避免了同樣的樣式在多個 class 裏定義多次導致代碼重複,並且局部作用於某個標籤,避免了全局污染。

它可以通過 @layer、@apply 或者插件的方式擴展原子 class,支持 prefix 來避免 class 名字衝突。

tailwind 本質上就是一個 postcss 插件,通過 AST 來分析 css 代碼,對 css 做增刪改,並且可以通過 extractor 提取 js、html 中的 class,之後基於這些來生成最終的 css 代碼。

是否感受到了 tailwind 的簡潔好用,易於擴展?就是這些原因讓它成爲了最流行的原子化 css 框架。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/kl8Dwo_jFGRU2VwE3Vn62g