時代變了,如今竟然能夠這樣寫 CSS 了|牛氣沖天新年徵文

文章永遠首發自個人 Github,你們能夠關注點贊,一般會早於發佈各大平臺一週時間以上。css

如今大部分搞前端的應該仍是這樣寫 CSS 的:html

.mock {
    margin: auto;
    font-size: 16px;
    // ...
}
<div class='mock'>mock</div>
複製代碼

以上代碼就是舉個例子,大部分狀況應該都是寫一個類,而後整一堆樣式進去。前端

可是這種方式寫多了之後,你應該會感覺到一些痛點,好比說:react

  • 取名困難,節點結構一多,取名真的是個難事。固然了,咱們能夠用一些規範或者選擇器的方式去規避一些取名問題。
  • 須要用 JS 控制樣式的時候又得多寫一個類,尤爲交互多的場景。
  • 組件複用你們都懂,可是樣式複用少之又少,這樣就形成了冗餘代碼變多。
  • 全局污染,這個其實如今挺多工具都能幫咱們自動解決了。
  • 死代碼問題。JS 咱們經過 tree shaking 的方式去除用不到的代碼減小文件體積,可是 CSS 該怎麼去除?尤爲當項目變大之後,無用 CSS 代碼總會出現。
  • 樣式表的插入順序影響了 CSS 究竟是如何生效的。
  • 等等,不一一說明了。其實對於筆者而言,第一二塊在開發中是最難受的兩個點,尤爲是剛寫前端,須要作活動 / 產品頁的時候。

當下,社區裏有一些 CSS 方案,可以解決以上一些痛點:git

  • Atom CSS
  • CSS-in-JS
  • 上述二者的結合體

本文就來聊聊以上三種方案的優缺點以及各自方案的表明做。github

Atom CSS

首先來聊聊啥叫作 Atom CSS:意思是一個類只幹一件事,好比說:設計模式

.m-8 {
    margin: 8px;
}
複製代碼

想象一下你按照這樣的思想搞出一大堆相似的類名,就能整出一個踐行 Atom CSS 方案的三方庫了,tailwindcss 就是這個方案裏的佼佼者。其實 Atom CSS 不少人應該早都用過了,柵格系統上就有它的身影,無非不清楚原來它就是 Atom CSS 罷了。緩存

咱們先來看看若是用 tailwindcss 的話,寫好樣式的 HTML 大概長啥樣:markdown

上圖是人家官網上的,在這以前還有一段挺炫的動畫。看起來好像挺方便的,寫上一堆類名就能出左邊好看的樣式了,省了不少寫樣式的時間,可是讀者們能夠來想一想這種方式它會有啥好處及弊端?app

在說優缺點以前,咱們先來聊聊 Atom CSS 的歷史。其實它並非一個新興產物,這玩意你往前推個十年就能看到它的討論。正所謂天道好輪迴,蒼天饒過誰。Atom CSS 之前火過,並且是被噴火的,沉寂了幾年以後這幾年又被拿出來講了。

接下來咱們以 tailwindcss 爲例來聊聊 Atom CSS 方案的優劣點。

優劣點

若是你想在團隊內部推廣這個產品,學習成本會是一個問題,畢竟須要你們都看得懂你這坨東西究竟是啥意思,這算一個很明顯的缺陷。可是對於語法問題你還真的不用怎麼擔憂,tailwindcss 是有語法補全的工具鏈的,Webstorm 已經內置了,VSCode 須要你們自行裝個插件,因此噴寫 tailwindcss 語法麻煩的能夠歇一歇。

樣式複用,就像寫組件同樣,此次咱們是把樣式一個個抽離了出來,這樣帶來的一大好處是減小了 CSS 代碼文件體積。

本來傳統的寫法是定義一個類,而後寫上須要的樣式:

.class1 {
    font-size: 18px;
    margin: 10px;
}
.class2 {
    font-size: 16px;
    color: red;
    margin: 10px;
}
複製代碼

這種寫法是存在一部分樣式重複的,換成 Atom CSS 就能減小一部分代碼的冗餘。

把 CSS 當成組件來寫。你們乍一看 tailwindcss 官網確定會以爲我在 HTML 裏寫個樣式要敲那麼多類是有病吧?

<figure class="md:flex bg-gray-100 rounded-xl p-8 md:p-0">
  <img class="w-32 h-32 md:w-48 md:h-auto md:rounded-none rounded-full mx-auto" src="/sarah-dayan.jpg" alt="" width="384" height="512">
  <div class="pt-6 md:p-8 text-center md:text-left space-y-4">
    <blockquote>
      <p class="text-lg font-semibold">
        「Tailwind CSS is the only framework that I've seen scale
        on large teams. It’s easy to customize, adapts to any design,
        and the build size is tiny.」
      </p>
    </blockquote>
    <figcaption class="font-medium">
      <div class="text-cyan-600">
        Sarah Dayan
      </div>
      <div class="text-gray-500">
        Staff Engineer, Algolia
      </div>
    </figcaption>
  </div>
</figure>
複製代碼

其實咱們是能夠利用 Atom CSS 一次只幹一件事的特性,將這些類隨意組裝成咱們想要的類,這樣就能夠提供出來一個更上層的通用樣式來複用。

好比說項目中的按鈕都是存在通用的圓角、內邊距、字體等,這樣咱們就能夠封裝出這樣一個類:

.btn {
    @apply p-8 rounded-xl font-semibold
}
複製代碼

效率工具。tailwindcss 用的好確定是能提升寫佈局的效率的,尤爲對於須要作響應式的頁面而言。固然這東西其實也算是甲之蜜糖乙之砒霜,評價兩極分化很嚴重,有人認爲提升了效率,也有人認爲反而是增長了成本,或者說是脫褲子放屁。

提供了一整套規範化的設計模式,直接點說就是 tailwindcss 給你內置好一套優秀的設計主題了。可是這玩意對於規範的視覺團隊來講是個不小的福音,不規範的話就多是火葬場了。下面我給你們舉個例子:

// tailwind.config.js
const colors = require('tailwindcss/colors')

module.exports = {
  theme: {
    screens: {
      sm: '480px',
      md: '768px',
      lg: '976px',
      xl: '1440px',
    },
    colors: {
      gray: colors.coolGray,
      blue: colors.lightBlue,
      red: colors.rose,
      pink: colors.fuchsia,
    },
    fontFamily: {
      sans: ['Graphik', 'sans-serif'],
      serif: ['Merriweather', 'serif'],
    },
    extend: {
      spacing: {
        '128': '32rem',
        '144': '36rem',
      },
      borderRadius: {
        '4xl': '2rem',
      }
    }
  }
}
複製代碼

以上是 tailwindcss 的主題配置文件,你們能夠按照視覺的要求來作調整。好比說今天視覺以爲屏幕的 lg 尺寸應該是 976px,過段時間又以爲須要改爲 1000px。對於開發者而言咱們只須要修改一行代碼就能全局生效了,很舒服。

可是假如說視覺本來定義的邊距規則以下:

// tailwind.config.js
module.exports = {
  theme: {
    spacing: {
      px: '1px',
      0: '0',
      0.5: '0.125rem',
      1: '0.25rem',
      1.5: '0.375rem',
      2: '0.5rem',
      2.5: '0.625rem',
      3: '0.75rem',
      3.5: '0.875rem',
      4: '1rem',
      5: '1.25rem',
      6: '1.5rem',
      7: '1.75rem',
      8: '2rem',
      // ...
    },
  }
}
複製代碼

如今須要咱們把 6 換成 1.6rem,可是這個規則只須要做用在某些組件上,此時咱們須要如何修改樣式?新增一個 spacing 而後一個個去替換須要的地方麼?

上述場景筆者認爲仍是很多見的,最起碼在咱們公司內部是存在這樣的問題。已經定義了視覺規範並體如今內部的組件庫上,可是在業務中仍是有很多視覺會去動組件的基本樣式,這裏改個邊距,那裏改個顏色等等。本來組件庫是爲了幫助開發者提效的,可是在這種場景下開發者反而會抱怨改動樣式極大提升了他們的成本,而且大部分狀況下還不得不這樣作。

再說回傳統 CSS 的問題,其實 tailwindcss 也解決了一部分,可是仍舊存在沒解決的點,好比說:

  • 死代碼問題沒解決
  • 樣式表的插入順序依舊有影響

以上說了那麼多,其實對於咱們使用 tailwindcss 而言,有利也有弊。它確定是存在很好用的場景的,好比說寫我的的產品頁,或者說業務中樣式變化不頻繁的場景中,可是若是說須要業務中全量切換到 tailwindcss 的話,筆者確定是持保留態度的。

對於 Atom CSS 來講,你們應該是不可否認它的優勢的,可是咱們是否有辦法在儘量避免它的缺點的狀況下又得到它的優勢呢?答案是有的,可是在講答案以前我想先來聊聊 CSS-in-JS。

CSS-in-JS

CSS-in-JS(下文以 CIJ 縮寫表示)核心就是在用 JS 寫 CSS,這一樣也是一個頗具爭議的技術方案。

在這個領域下有兩個庫比較流行,分別爲:styled-components(下文以 sc 縮寫表示) 以及 Emotion。筆者目前已經用了一年多的 sc 了,來粗略談談它的優缺點。

咱們先來了解下 sc 是怎麼使用的。首先說下 sc 和 Emotion 的語法是趨於一致的,應該是爲了 API 層面的統一吧,甚至前者還依賴了後者的一些包,如下是 sc 的經常使用寫法:

const Button = styled.a` display: inline-block; ${props => props.primary && css` background: white; color: black; `} `
render(
  <div> <Button href="https://github.com/styled-components/styled-components" target="_blank" rel="noopener" primary > GitHub </Button> <Button as={Link} href="/docs"> Documentation </Button> </div>
)
複製代碼

用法咱們很少展開,有興趣的能夠去官方看看,基本沒有學習成本的,主要是一些樣式組件上的使用。

另外 sc 並非最終生成了內聯樣式,而是幫咱們插入了 style 標籤。

優劣點

筆者用了一年多的 sc,感受這種方案對於 React 來講是很香的。而且解決了我很討厭的傳統寫 CSS 的一些點,因此關於優劣點這段的講述會有點主觀。

首先 CSS-in-JS 這種方案不只能讓咱們完整使用到 CSS 的功能,並且還擴充了一些用法。好比說選擇器這塊,在 sc 中咱們能經過選擇組件的方式來編寫樣式,以下代碼:

const Button = styled.a` ${Icon} { color: green; } `
複製代碼

另外既然咱們經過 JS 來管理 CSS 了,那麼咱們就能夠充分享受 JS 帶來的工具鏈好處了。一旦項目中出現沒有使用到的樣式組件,那麼 ESLint 就能夠幫助咱們找到那些死代碼並清除,這個功能對於大型項目來講仍是能減小一部分代碼體積的。

除此以外,樣式污染、取名問題、自動添加前綴這些問題也很好的解決了。

除了以上這些,再來聊兩點不容易注意到的。

首先是動態切換主題。由於咱們是經過 JS 來寫 CSS 了,那麼咱們就能夠動態地控制樣式。若是你的項目有切換主題這種相似的大量動態 CSS 的需求,那麼這個方案會是一個不錯的選擇。

還有個點是按需加載。由於咱們是經過 JS 寫的 CSS,現階段打包基本都走的 code split,那麼就能夠實現 CSS 文件的按需加載,而不是傳統方式的一次性所有加載進來(固然也是能夠優化的,只是沒那麼方便)。

聊完了優勢咱們再來講說缺點。

第一個缺點很明顯,有學習成本,固然筆者以爲這個學習曲線仍是平緩的。

運行時成本,sc 自己就有文件體積,加上還須要動態生成 CSS,那麼這其中一定有性能上的損耗。項目越大影響的也會越大,若是你的項目對於性能有很高的要求,那麼須要謹慎考慮使用。另外由於 CSS 動態生成,因此不能像傳統 CSS 同樣緩存 CSS 文件了。

代碼複用性和傳統寫 CSS 的方式沒啥兩樣。

最後點是代碼耦合問題。會有人以爲在大型項目中將 CSS 及 JS 寫在一塊兒會增長維護成本,而且也不符合 CSS 須要分離開來想法。

Atom CSS 加上 CSS-in-JS 的縫合怪

看了上文,若是你以爲兩種方案都挺好的話,能夠了解下 twin.macro,這個庫(還有別的競品)把這兩種方案融合了起來。

import 'twin.macro'

const Input = () => <input tw="border hover:border-black" />
const Input = tw.input`border hover:border-black`
複製代碼

這種方案之上其實還有更好玩的方式,能幫助咱們儘可能取其精華而去其糟粕。

自動生成 Atom CSS 的 CSS-in-JS 方案

假如說我不只想用 CSS-in-JS,還想把 Atom CSS 也給整上,可是又不想記 / 寫一大堆類名,我這個想法能實現麼?

答案是有的。利用運行時的方式把單個樣式抽離出來,最後實現雖然咱們寫的是 CSS-in-JS,可是最終呈現的是 Atom CSS 的樣子。

styletron 舉個例子,開發時候的代碼長這樣:

import { styled } from "styletron-react";

export default () => {
  // Create a styled component by passing
  // an element name and a style object
  const Anchor = styled("a", {
    fontSize: "20px",
    color: "red"
  });
  return <Anchor href="/getting-started">Start!</Anchor>;
};
複製代碼

實際編譯出來的時候長這樣:

<html>
  <head> <style> .foo { font-size: 20px; } .bar { color: red; } </style> </head>
  <body> <a href="/getting-started" class="foo bar">Start!</a> </body>
</html>
複製代碼

這樣的方式就能很好地享受到兩種方案帶來的好處了。可是這類方案筆者找了些競品,以爲尚未前二者方案來的流行,你們瞭解一下便可。另外這種方式帶來的運行時成本應該會更大,也許能夠配套打包工具在本地先作一次預編譯(一個不成熟的想法,說錯勿噴)?

總結

說了那麼多方案,可能讀者會有疑問,那麼我到底該用啥?這裏筆者說下本身的想法。

首先對於 sc 來講,筆者以爲很香,在項目中大範圍用起來何嘗不可,固然咱們還能夠搭配着 Atom CSS 一塊兒來寫通用樣式。

對於 Atom CSS,筆者我的認爲不適合項目中大規模使用,起碼在咱們公司內部不會是一個好方案,畢竟視覺真的會來動某些通用樣式。

你們也能夠來講說各自的見解。

本文同步更新於公衆號「前端真好玩」,歡迎關注。

相關文章
相關標籤/搜索