React CSS-In-JS 方案: Linaria Vs. Styled Components

在開發(fā)一個 React 應(yīng)用時,其中一個比較大的挑戰(zhàn)就是為應(yīng)用選擇一個合適的樣式處理方案。因為我們需要考慮到樣式的可維護性,開發(fā)體驗,以及樣式對應(yīng)用性能的影響等。基于這些考慮,很多開發(fā)者會選擇使用 CSS-in-JS 方案。CSS-in-JS 方案將 javascript 作用于編寫應(yīng)用樣式上。這有利于提升樣式的可維護性,在編寫樣式過程中使用更加模塊化的方式,將「動態(tài)樣式」引入 react 應(yīng)用中。目前市面上有非常多的 CSS-in-JS 方案。本文選擇了使用比較多的兩個方案 LinariaStyled-components 進行比較

在本文中,我們將回顧這兩個流行的 CSS-in-JS 方案: LinariaStyled-components 。我們將一起研究他們的功能,同時比較他們功能差異,性能以及生態(tài)。

CSS-in-JS 解決方案

CSS-in-JS 解決方案給我們提供了新的編寫 CSS 的方式。這些方案使用以 javascript 為基礎(chǔ)的 API 來創(chuàng)建和編寫樣式。主要的優(yōu)點包括:

  • 動態(tài)樣式:允許開發(fā)者編寫動態(tài) CSS
  • 元素作用域:可以把樣式的范圍固定在某些元素上
  • 消除無用代碼:會自動除去應(yīng)用中冗余的 CSS 代碼
  • 支持自定義主題
  • 安裝和設(shè)置簡單
  • 支持 ES modules 和 作用域
  • 更加容易編寫單元測試
  • 性能提升
  • 支持 SSR
  • 支持所有的 CSS 語法

Linaria

Linaria 是最流行的 CSS-in-JS 解決方案之一,GitHub 擁有 7.1k 個 star 和 260 個 fork。Linaria 是 「零運行時」方法,這意味著它將開發(fā)者寫好的樣式代碼在構(gòu)建時轉(zhuǎn)換為一個單獨的 .css 文件。這個行為跟很多的 CSS 預(yù)處理器相似,比如 SASS ,LESS

它提供了很多功能,包括:

  • 提供創(chuàng)建 CSS 類的 API 。”css” API 允許開發(fā)者創(chuàng)建所選擇的樣式,同時他也支持模版語法來滿足當我們需要插入動態(tài)值。
import { css } from '@linaria/core';

const red = "red"
const header = css`
  text-transform: uppercase;
    color: ${red}
`;

<h1 className={header}>Hello world</h1>;
0.png
  • 它也提供可以創(chuàng)建元素的 API. “styled” API 允許開發(fā)者創(chuàng)建任何元素,比如: div,p,等等。當然,這個 API 也是支持模版語法來插入對應(yīng)的變量值的
import { styled } from '@linaria/react'

const Container = styled.div`
  font-size: 35px;
  color: red;
  border: 1px solid red;

  &:hover {
    border-color: blue;
  }

  h1 {
    margin-bottom: 24px;
  }
`;

const App = () => {
    return <Container>
        <h1>Hello World</h1>
    </Container>
  }
export default App;
1.png
  • 它通過 React 的 Props 或者常規(guī)變量來管理動態(tài)樣式。在下面的代碼中,我們通過 React 組件傳遞 props 和一些常規(guī)變量到另一個元素上
import { styled } from '@linaria/react';

const Title = styled.h1`
  font-family: inherit;
`;
const medium = 30

const Navbar = styled.nav`
  font-size: ${medium}px;
  color: ${props => props.color};
  border: 1px solid red;

  &:hover {
    border-color: blue;
  }

  ${Title} {
    margin-bottom: 24px;
  }
`;

const App = () => {
    return <Navbar color="#999">
        <Title>Hello world</Title>
    </Navbar>
}

export default App;
2.png

其他的功能包括:

  • 通過 CSS source maps 可以很容易的找到樣式變量是在哪里定義的
  • 可以在 JS 代碼中開啟 CSS Lint https://github.com/stylelint/stylelint
  • 通過 @linaria/atomic 可以支持原子樣式

****Styled-Components****

Styled-Components 也是流行的 CSS-in-JS 解決方案之一。在 GitHub 上擁有 37.2 k 的 star 和 2.3 k 的 forks。Styled-components 讓開發(fā)者能夠通過編寫真實的 CSS 代碼來修改組件的樣式。它在組件和樣式之間創(chuàng)建了一個抽象層,從而消除了直接的映射。

提供的能力,包括:

  • 自動提取關(guān)鍵 CSS 和 代碼分割:Styled-Components 監(jiān)控組件,并且在組件渲染到頁面的時候插入組件必要的樣式代碼。同時支持代碼分割來加快組件加載的速度
  • 為樣式生成唯一的類名,以防止樣式的覆蓋,拼寫錯誤以及冗余
  • Styled-component 也提供通過 props 或者常規(guī)變量為元素注入動態(tài)值。“styled” API 允許開發(fā)者創(chuàng)建選擇的元素,跟 Linaria 一樣,Styled-component 也支持大致相同的模版語法
import styled from 'styled-components';

const Button = styled.button`
  background: ${props => props.primary ? "palevioletred" : "white"};
  color: ${props => props.primary ? "white" : "palevioletred"};

  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border: 2px solid palevioletred;
  border-radius: 3px;
`;

const App = () => {
    return <div>
        <Button>Normal</Button>
        <Button primary>Primary</Button>
  </div>
}

export default App;
3.png
  • 樣式擴展:Styled-Components 允許在已有的樣式上通過 styled 進行擴展
import styled from 'styled-components';

const Button = styled.button`
  color: palevioletred;
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border: 2px solid palevioletred;
  border-radius: 3px;
`;

const TomatoButton = styled(Button)`
  color: tomato;
  border-color: tomato;
`;

const App = () => {
    return <div>
        <Button>Normal Button</Button>
        <TomatoButton>Tomato Button</TomatoButton>
    </div>
}

export default App;
3-1.png
4.png

其他的功能,包括:

  • 維護成本低
  • 更簡便的刪除不必要的 CSS
  • 支持 SSR
  • 支持主題定制

對比 Linaria 和 Styled-Components

開發(fā)者在很長一段時間都在選擇最適合自己項目的樣式解決方案,而 Linaria 和 Styled-Components 無疑是當中的佼佼者。接下來我們將從:「功能」,「性能」以及 「生態(tài)」來對這兩個方案進行比較

功能

  • Linaria 是「零運行時」方案,這意味著樣式文件會在構(gòu)建時被單獨抽取成 CSS 文件;Styled-Components 則是在構(gòu)建時通過 javascript 將 CSS 注入,不會生成額外的 CSS 文件
  • 都擁有相似的 CSS 語法(類似 Sass 的風格)
  • 在 React 應(yīng)用中都可以基于 Prop 實現(xiàn)變量注入(原理是使用 CSS 變量)
  • 都可以通過 CSS source maps 很快找到 CSS 變量定義的位置
  • 都可以通過 stylelint 的方案進行代碼檢查
  • 都使用 Javascript 來組織代碼邏輯,無需使用 CSS 預(yù)處理器
  • 都可以平替 Sass 或者 PostCSS 方案

通過上述的的功能描述,我們發(fā)現(xiàn) Linaria 和 Styled-Components 的 API 都比較相似,所以開發(fā)者很容易就可以從其中一個方案遷移到另一個方案

基于請求的性能對比

  • 在生產(chǎn)環(huán)境中, Linaria 會產(chǎn)生額外的 .css 文件,這將會引起 CSS 文件體積變大,文件數(shù)量變多,導(dǎo)致請求數(shù)量變多的問題
  • 對于 Styled-Components 來說,相同情況下,CSS 文件體積和數(shù)量無疑是更少的,但是會增加 JS bundle 的體積大小

許多爭論在于認為 Linaria 產(chǎn)生的 css 文件對性能的影響是比較小的,相對于 Styled-Components ,Linaria 不會增加 JS bundle 體積是一種更好的取舍;而另一些則認為 Linaria 增加了 CSS 冗余代碼的可能性。

我們可以在 這里 看到更多關(guān)于請求的性能對比

基于頁面加載的性能對比

在加入多種頁面加載標準之后發(fā)現(xiàn),大部分的頁面使用 Linaria 的加載性能要 好于 使用 Styled-Components。其中一個比較重要的原因就是,Linaria 導(dǎo)致的 CSS 資源體積與數(shù)量的增加對于頁面加載的影響要小于 Styled-Components 導(dǎo)致的 JS bundle 體積的增加

我們可以從下面的資料中看到更多關(guān)于加載的對比

基于渲染和用戶交互的性能對比

在這個方面的對比,主要是頁面元素拖拽交互以及重新渲染;結(jié)果顯示大部分的 Linaria 會有更少的腳本運行時間,更少的樣式重繪重排

我們可以從下面的資料中看到更多關(guān)于渲染的對比

生態(tài)系統(tǒng)

Styled-components 目前擁有 37.2K GitHub stars, 2.3K GitHub forks,超過 4百萬的 NPM 包周下載量,可以說是 CSS-in-JS 最大的生態(tài)系統(tǒng)方案;而 Linaria 只有 7.1K GitHub stars, 260 GitHub fork 和 16000 的 NPM 包周下載量。這意味著 Styled-components 會有更大的社區(qū)以及討論熱度,更多的課程(學(xué)習(xí)成本低)以及更多的問題解答等等

總結(jié)

本文我們先介紹了 Linaria 和 Styled-Component 的使用。然后又對比兩者之間的功能,性能特點以及生態(tài)系統(tǒng)。

本文的代碼地址:

參考資料

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,732評論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,214評論 3 426
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,781評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,588評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,315評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,699評論 1 327
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,698評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,882評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,441評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,189評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,388評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,933評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,613評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,023評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,310評論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,112評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,334評論 2 377

推薦閱讀更多精彩內(nèi)容