在開發(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 方案。本文選擇了使用比較多的兩個方案 Linaria 和 Styled-components 進行比較
在本文中,我們將回顧這兩個流行的 CSS-in-JS 方案: Linaria 和 Styled-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>;
- 它也提供可以創(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;
- 它通過 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;
其他的功能包括:
- 通過 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;
- 樣式擴展: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;
其他的功能,包括:
- 維護成本低
- 更簡便的刪除不必要的 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)于加載的對比
- https://github.com/geeky-biz/css-in-js-benchmark?ref=reactjsexample.com#page-loading-performance
- https://pustelto.com/blog/css-vs-css-in-js-perf/#lighthouse-performance-audit
基于渲染和用戶交互的性能對比
在這個方面的對比,主要是頁面元素拖拽交互以及重新渲染;結(jié)果顯示大部分的 Linaria 會有更少的腳本運行時間,更少的樣式重繪重排
我們可以從下面的資料中看到更多關(guān)于渲染的對比
- https://github.com/geeky-biz/css-in-js-benchmark?ref=reactjsexample.com#re-rendering-performance
- https://pustelto.com/blog/css-vs-css-in-js-perf/#comparing-user-interaction
生態(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)。
本文的代碼地址: