成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

為什么我要含淚揮別 CSS-in-JS?

開發 前端
本文深入探討了運行時CSS-in-JS的優點和缺點。作為開發人員,我們需要評估這些優缺點,然后就該技術是否適合使用案例做出明智的決定。對于我來說,Emotion的運行時性能成本遠遠超過了DX方面的好處,尤其是考慮到Sass模塊+實用程序類的替代方案仍然具有良好的DX,同時也提供了非常卓越的性能。

這篇文章將深入探討最初吸引我使用CSS-in-JS以及后來又決定放棄的原因。如果你對CSS-in-JS背后的邏輯感興趣,建議耐心看完本文。

CSS-in-JS是什么?

顧名思義,CSS-in-JS允許你通過在JavaScript或TypeScript代碼中直接編寫CSS來設置React組件的樣式:

// @emotion/react (css prop), with object styles
function ErrorMessage({ children }) {
return (
<div
css={{
color: 'red',
fontWeight: 'bold',
}}
>
{children}
</div>
);
}

// styled-components or @emotion/styled, with string styles
const ErrorMessage = styled.div`
color: red;
font-weight: bold;
`;

styled-components[1]和Emotion[2]是React社區中最受歡迎的CSS-in-JS庫。

本文重點介紹運行時CSS-in-JS,包括styled-components和Emotion。運行時CSS-in-JS僅表示庫在應用程序運行時解析并應用樣式。并將在本文末尾簡要討論編譯時CSS-in-JS。

CSS-in-JS的優缺點

優點

1.可以限定樣式應用的范圍。 在編寫純CSS時,很容易不小心擴大樣式的應用范圍。例如,假設你正在創建一個列表視圖,每行都需要有一些填充和邊框。你可能會這樣寫CSS:

.row {
padding: 0.5rem;
border: 1px solid #ddd;
}

幾個月后,你完全忘記了這個列表視圖,又創建了另一個具有列表行的組件。當然,你可以在這些元素上設置className="row"。現在,新組件的列表行有一個難看的邊框,你不知道為什么!雖然這種類型的問題可以通過使用更長的類名或更具體的選擇器來解決,但作為開發人員,你需要確保沒有類名沖突。

CSS-in-JS通過控制樣式的默認應用范圍來完全解決此問題。如果將列表視圖行編寫為:

<div css={{ padding: '0.5rem', border: '1px solid #ddd' }}>...</div>

那么,填充和邊框就不可能意外地應用于不相關的元素。

注意:CSS模塊也提供本地范圍的樣式。

2.集中放置。 如果使用普通CSS,那么你可以把所有.css文件放在src/styles目錄,同時所有React組件放在src/components中。但是隨著應用程序規模的增長,很快就會難以判斷每個組件使用哪種樣式。于是這樣的CSS很多時候就會變成死代碼,因為沒有簡單的方法可以判斷這些樣式有沒有被使用過。

組織代碼的更好方法是,將與單個組件相關的所有內容都放在同一位置。這種做法稱為集中放置。

問題在于,這在使用純CSS時很難實現,因為CSS和JavaScript必須放在單獨的文件中,并且無論.css文件位于何處,都將全局應用樣式。另一方面,如果使用的是CSS-in-JS,則可以直接在使用它們的React組件中編寫樣式!如果操作正確,這將大大提高應用程序的可維護性。

注意:CSS模塊還允許將樣式與組件一起放置,即使不在同一文件中。

3.可以在樣式中使用腳本變量。 CSS-in-JS使你能夠在樣式規則中引用JavaScript變量,例如:

// colors.ts
export const colors = {
primary: '#0d6efd',
border: '#ddd',
/* ... */
};

// MyComponent.tsx
function MyComponent({ fontSize }) {
return (
<p
css={{
color: colors.primary,
fontSize,
border: `1px solid ${colors.border}`,
}}
>
...
</p>
);
}

如本例所示,你可以在CSS-in-JS樣式中使用JavaScript常量(例如colors)和React prop/state(例如fontSize)。在某些情況下,在樣式中使用JavaScript常量的功能減少了重復代碼,因為不必將同一常量既定義為CSS變量又定義為JavaScript常量。使用props和state的功能允許你創建具有高度可自定義樣式的組件,而無需使用內聯樣式。(當相同的樣式應用于許多元素時,內聯樣式對于性能并不友好。)

缺點

1.CSS-in-JS會增加運行時開銷。當呈現組件時,CSS-in-JS庫必須將樣式“序列化”為可插入到文檔中的純CSS。很明顯,這會占用額外的CPU開銷。

2.CSS-in-JS會增加Bundle的大小。這是一個顯而易見的問題——每個訪問網站的用戶都必須下載CSS-in-JS庫。Emotion是7.9kB壓縮包,而styled-components是12.7kB。雖然這兩個庫都不是很大,但加在一起就不是了(react + react dom是44.5kB)。

3.CSS-in-JS會擾亂React開發工具。對于使用css prop的每個元素,Emotion將呈現<EmotionCssPropInternal>和<Insertion>組件。如果你在許多元素上使用css prop,那么Emotion的內部組件確實會使React DevTools變得混亂,如下所示:

圖片

最可怕的幾個地方

1.頻繁插入CSS會迫使瀏覽器做很多額外的工作。在并發渲染中,React將會在渲染之間讓步于瀏覽器。如果你在組件中插入一個新CSS規則,那么瀏覽器必須先查看這些CSS規則是否適用于現有的DOM樹,因此會重新計算樣式規則。之后React渲染下一個組件,該組件發現一個新規則,然后同樣的情況再次發生。

這就導致了在React渲染時每幀針對所有DOM節點會重新計算所有CSS規則。

關于這個問題最糟糕的是,這不是一個可修復的問題(在運行時CSS-in-JS的上下文中)。運行時CSS-in-JS庫通過在組件呈現時插入新的樣式規則來工作,這對基本的性能是不利的。

2.使用CSS-in-JS可能會出現更多錯誤,尤其是在使用SSR或組件庫時。在Emotion GitHub存儲庫中,存在的問題有:

  • 一次加載多個Emotion實例。
  • 組件庫通常不能讓你完全控制樣式的插入順序。
  • Emotion的SSR支持在React 17和React 18之間的工作方式不同。這對于與React 18的流式服務端渲染兼容是必要的。

這些缺點還只是冰山一角。

深入探討性能

很明顯,運行時CSS-in-JS既有重要的優點,也有顯著的缺點。為了說明為什么我選擇遠離這項技術,我們需要探索CSS-in-JS對實際性能的影響。

渲染內部 vs. 渲染外部的序列化

樣式序列化是指Emotion獲取CSS字符串或對象樣式并將其轉換為可插入到文檔中的純CSS字符串的過程。Emotion還會在序列化期間計算普通CSS的哈希值——此哈希值是你在生成的類名中所看到的內容,例如.css-15nl2r3。

Emotion文檔在渲染中執行序列化的示例,如下所示:

function MyComponent() {
return (
<div
css={{
backgroundColor: 'blue',
width: 100,
height: 100,
}}
/>
);
}

每次渲染MyComponent時,都會再次序列化對象樣式。如果MyComponent頻繁渲染(例如,在每次擊鍵時),那么重復的序列化可能會產生很高的性能開銷。

提高性能的方法是將樣式移到組件外部,以便在加載模塊時進行一次序列化,而不是在每次渲染時都進行序列化。@emotion/react的css函數可以做到這一點:

const myCss = css({
backgroundColor: 'blue',
width: 100,
height: 100,
});

function MyComponent() {
return <div css={myCss} />;
}

當然,這會阻止你訪問樣式中的prop,因此會錯過CSS-in-JS的主要優點之一。

在使用Emotion時,對Member瀏覽器進行基準測試

下面是一個關于Member瀏覽器的簡單列表視圖。幾乎所有Member瀏覽器的樣式都使用Emotion,特別是css prop。

圖片

在此次測試中:

Member瀏覽器將顯示20個用戶,

將刪除列表項周圍的React.memo,

強制最頂層<BrowseMembers>組件每秒渲染一次,并記錄前10次渲染的時間。

關閉React嚴格模式。

使用React DevTools分析該頁面,前10次渲染時間的平均值為54.3毫秒。

我個人的經驗法則是,React組件的渲染時間應該為16毫秒或更短的時間,因為以每秒60幀的速度渲染,渲染1幀是16.67毫秒。Member瀏覽器目前是這個數字的3倍以上,所以是一個非常重量級的組件。

該測試是在M1 Max CPU上執行的,此CPU比普通用戶擁有的CPU快得多。在功能較弱的計算機上,54.3毫秒的渲染時間甚至很容易達到200毫秒。

分析火焰圖

以下是上述測試中單個列表項的火焰圖:

圖片

正如你所看到的,有大量的<Box>和<Flex>組件正在渲染——這些都使用css prop。雖然每個<Box>組件只需要0.1–0.2毫秒的渲染時間,但因為<Box>組件的總量大,所以總的耗時將是巨大的。

在沒有用Emotion時,對Member瀏覽器進行基準測試

為了了解這種昂貴的渲染代價有多少是由于Emotion造成的,我重寫了Member瀏覽器樣式,使用的是Sass模塊而不是Emotion。(Sass模塊在構建時編譯為普通CSS,因此使用時幾乎沒有性能損失。)

重復與上述相同的測試,前10次渲染的平均值是27.7毫秒。比原來減少了48%!

所以,這就是我們與CSS-in-JS說再見的原因:運行時性能成本太高了。

免責聲明:如果你的代碼庫以更高性能的方式使用Emotion(例如,在渲染之外進行樣式序列化),那么移除CSS-in-JS后,看到的性能提升可能并不顯著。

如果你對此測試感興趣,以下是原始數據:

圖片

新的樣式系統

在下定決心要擺脫CSS-in-JS之后,有一個問題馬上擺在了我們面前:那么用什么來代替呢?理想情況下,我們想要一個性能類似于普通CSS的樣式系統,同時盡可能多地保留CSS-in-JS的好處。也就是說最好具有:

可以控制樣式的應用范圍。

樣式與應用樣式的組件放在一起。

可以在樣式中使用腳本變量。

前面我說過CSS模塊也提供樣式應用范圍控制和集中放置的能力。CSS模塊編譯為普通的CSS文件,因此使用它們沒有運行時性能成本。

但是,CSS模塊的主要缺點是,歸根結底,它們仍然是普通的CSS——而普通的CSS缺乏改善DX和減少代碼重復的功能。

幸運的是,這個問題有一個簡單的解決方案——Sass模塊,用Sass編寫的CSS模塊。獲得CSS模塊的本地范圍樣式和Sass強大構建功能的同時,基本上沒有運行時成本。這就是為什么Sass模塊將成為我們未來的通用樣式解決方案的原因。

實用程序類

我們團隊對從Emotion切換到Sass模塊的一個擔憂是,應用常見樣式,如display: flex會不太方便。以前:

<FlexH alignItems="center">...</FlexH>

如果僅使用Sass模塊執行此操作,則必須打開.module.scss文件并創建一個應用樣式display: flex和align-items: center的類。

為了改進這方面的DX,我們決定引入一個實用程序類系統。實用工具類是在元素上設置單個CSS屬性的CSS類。通常組合多個實用程序類以獲得所需的樣式。上面的示例可以編寫為:

<div className="d-flex align-items-center">...</div>

Bootstrap和Tailwind是提供實用程序類的最流行的CSS框架。我已使用Bootstrap多年,所以選擇Bootstrap。雖然可以將Bootstrap實用程序類作為預構建的CSS文件引入,但我們需要自定義類以適應現有的樣式系統,因此我將Bootstrap源代碼的相關部分復制到了項目中。

將Sass模塊和實用程序類用于新組件,我已經使用幾個星期了,感到非常滿意。DX與Emotion相似,但運行時性能要優越得多。

附注:還可以使用typed-scss-modules[3]來為Sass模塊生成類型腳本定義。這樣做的最大好處是,允許我們定義一個像classnames[4]一樣工作的utils()幫助函數。但是不方便的一點是,它只接受有效的實用程序類名作為參數。

關于編譯時CSS-in-JS的說明

本文重點介紹運行時CSS-in-JS庫,如Emotion和樣式化組件。最近,我發現有越來越多的CSS-in-JS庫在編譯時將樣式轉換為純CSS。包括:

  • Compiled[5]
  • Vanilla Extract[6]
  • Linaria[7]

這些庫旨在提供與運行時CSS-in-JS類似的優勢,而不會降低性能成本。

雖然我自己沒有使用過編譯時CSS-in-JS庫,但我仍然認為與Sass模塊相比,它們有缺點。以下是我在查看編譯時看到的缺點:

  • 當組件首次掛載時,仍會插入樣式,這會強制瀏覽器在每個DOM節點上重新計算樣式。
  • 動態樣式(如示例中的color prop)無法在構建時提取,因此編譯時使用style prop(也稱為內聯樣式)將值作為CSS變量添加。已知內聯樣式在應用許多元素時會導致性能欠佳。
  • 庫仍會將樣式組件插入到React樹中。這將使React開發工具變得混亂,就像運行時CSS-in-JS一樣。

總結

本文深入探討了運行時CSS-in-JS的優點和缺點。作為開發人員,我們需要評估這些優缺點,然后就該技術是否適合使用案例做出明智的決定。對于我來說,Emotion的運行時性能成本遠遠超過了DX方面的好處,尤其是考慮到Sass模塊+實用程序類的替代方案仍然具有良好的DX,同時也提供了非常卓越的性能。

參考資料

[1]styled-components: https://styled-components.com/

[2]Emotion: https://emotion.sh/

[3]typed-scss-modules: https://www.npmjs.com/package/typed-scss-modules

[4]classnames: https://www.npmjs.com/package/classnames

[5]Compiled: https://compiledcssinjs.com/

[6]Vanilla Extract: https://vanilla-extract.style/

[7]Linaria: https://linaria.dev/

責任編輯:武曉燕 來源: 前端新世界
相關推薦

2022-11-11 08:16:51

2021-02-11 09:01:32

CSS開發 SDK

2022-09-22 16:03:07

CSS-in-JS代碼

2012-06-18 14:51:09

Python

2023-11-01 08:36:07

CSSTailwind

2022-03-22 09:07:34

開發CSS技術

2022-12-13 09:59:25

計算遷移

2025-01-15 09:06:58

CSSRegEx前端

2019-05-14 11:15:51

微軟員工上市公司

2020-03-03 15:31:47

ReactVue前端

2019-04-22 14:20:08

區塊鏈數字貨幣比特幣

2009-09-29 16:24:11

2017-05-19 16:40:41

AndroidKotlin開發者

2016-05-18 10:15:25

PythonNode.js

2015-08-06 10:14:15

造輪子facebook

2022-08-15 08:27:02

基站網絡

2013-03-12 14:30:09

Ubuntu操作系統

2017-10-25 09:50:51

Linux

2020-11-16 08:16:08

外企國企工作

2012-04-04 22:07:12

Android
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 免费性视频 | 久久黄色网 | 国产福利在线免费观看 | 这里有精品 | 亚洲欧美v | 婷婷久久网 | 欧美日韩中文字幕在线 | 欧美国产日韩一区二区三区 | 天天澡天天狠天天天做 | www.中文字幕.com | 国产精品国产 | 亚洲国产一区二区三区 | 免费在线视频一区二区 | 欧美成人一区二区三区 | 亚洲人成人网 | 免费人成在线观看网站 | 亚洲国产欧美精品 | 国产一区二区三区四区五区加勒比 | 精品在线 | 亚洲视频一区在线观看 | 中文字幕精品视频 | 欧美自拍一区 | 蜜桃视频麻豆 | 亚洲精品视频二区 | 视频在线一区二区 | 欧美精品黄 | 欧美中文视频 | 国产一区二区三区在线 | 精品国产乱码一区二区三区 | 久草网址 | 一区二区国产在线观看 | 亚洲精品乱码久久久久久久久久 | 国产一区二区三区久久久久久久久 | 欧美日韩中 | 久久国产精品一区 | 欧美一级视频免费看 | 日韩高清av | 国产a区| 精品永久 | 久久久视 | 一级在线免费观看 |