CSS in JS 新秀:Vanilla-Extract 淺析
本文轉(zhuǎn)載自微信公眾號「Tecvan」,作者江江。轉(zhuǎn)載本文請聯(lián)系Tecvan公眾號。
前言
2021年,vanilla-extract 作為黑馬登頂了 css-in-js 滿意度榜首(雖然使用率僅為1%),號稱是一個類型安全、高度兼容 TS 場景的庫,國內(nèi)相關(guān)討論還很少,稍微看了一下還挺好用的。
介紹
官方文檔:https://vanilla-extract.style/documentation/
打開vanilla-extract官網(wǎng)文檔,里面已經(jīng)羅列了他的那些優(yōu)點。作為一個如果使用 css-in-js 會首選styled-components的我來說,比較關(guān)注的點主要是:
- All styles generated at build time
- Type-safe styles via CSSType.
這兩點說白了就是,零運(yùn)行時且支持typescript。
零運(yùn)行時:vanilla-extract會在編譯時期,編譯出 css modules值和css內(nèi)容,不需要帶任何運(yùn)行時內(nèi)容到生產(chǎn)環(huán)境,相對來說運(yùn)行速度更高,產(chǎn)物體積更小;
typescript:支持typescript類型檢查,CSS安全;
目前,業(yè)界大多數(shù)相關(guān)競品如 styled-components就是一個運(yùn)行時方案且基于標(biāo)簽?zāi)0暹M(jìn)行書寫,主要基于stylis解析器解析,如果頻繁更新props還會造成style標(biāo)簽大量插入到head里。
安裝
Webpack 環(huán)境下,需要同時安裝 @vanilla-extract/css 與 @vanilla-extract/webpack-plugin 插件:
- yarn add @vanilla-extract/css @vanilla-extract/webpack-plugin
安裝完成后,修改 Webpack 配置:
- const { VanillaExtractPlugin } = require('@vanilla-extract/webpack-plugin')
- module.exports = {
- entry: './src/index.js',
- // ....
- plugins: [new VanillaExtractPlugin()]
- };
這里可以看見,當(dāng)我們使用 vanilla-extract 時,需要安裝兩個庫:
- @vanilla-extract/css:開發(fā)核心庫,基于該庫進(jìn)行項目業(yè)務(wù)樣式開發(fā);
- @vanilla-extract/webpack-plugin:webpack插件。前面也提及了vanilla-extract是一個零運(yùn)行的庫,主要是通過該插件處理。
- 通過增加webpack配置項,對相關(guān)后綴文件使用自定義的@vanilla-extract/webpack-plugi/loader進(jìn)行處理。
- 在其內(nèi)部通過eval庫在編譯時先執(zhí)行得到className結(jié)果,避免在運(yùn)行時執(zhí)行過程;
- 再移除@vanilla-extract/css庫,使其不會影響到生產(chǎn)環(huán)境下js包體大小;
構(gòu)建樣式API
這里只會對vanilla-extract比較核心的構(gòu)建樣式相關(guān)幾個API提及,其他API可以直接前往官網(wǎng)查看。
style
- import { style } from '@vanilla-extract/css';
- export const parentClass = style({
- background: 'red',
- ':hover': {
- background: 'blue',
- },
- });
- export const childClass = style({
- selectors: {
- '&:nth-child(2n)': {
- background: '#fafafa',
- },
- [`${parentClass} &`]: {
- color: 'pink',
- },
- },
- });
- import { childClass, parentClass } from './index.styles.css';
- const Demo = () => (
- <div className={parentClass}>
- <div className={childClass}>DEMO1</div>
- <div className={childClass}>DEMO2</div>
- <div className={childClass}>DEMO3</div>
- </div>
- );
- export default Demo;
這個簡單的demo我相信,看幾下就已經(jīng)明白是怎么使用了。但是需要理解的地方是,為了提高可維護(hù)性,「每個樣式塊只能針對某個元素(或者說是使用這個樣式塊的元素)」。那么在上述代碼里的selectors而言,「其目標(biāo)必須是」**&**(也就是自身元素)而不能是其他元素。例如:`${parentClass} &`是OK的,但是`& div`是不允許的。這樣的設(shè)計,我覺得更是一種職責(zé)分離吧,每個樣式塊都針對某個元素,那么對于項目而言,樣式的可維護(hù)性就大大提高了,相比于其他css in js(styled-components)就不容易出現(xiàn)樣式冗余的問題。對于一些特殊情況,比如:在寫styled-components我們會利用其包裹arco組件(或是其他組件),然后對其內(nèi)部元素樣式進(jìn)行覆寫或是新增。
- const StyledSelect = styled(Select)`
- div {
- color: red;
- }
- `
那么在vanilla-extract樣式塊里是不能直接做到的,因為每個樣式塊都是針對某個元素,是不能直接通過該樣式塊,直接對其兄弟元素、子元素進(jìn)行樣式調(diào)整。但是,對于這種情況,是可以使用另一個APIglobalStyle進(jìn)行開發(fā)。
globalStyle
顧名思義,就是全局樣式API。但是因為本身vanilla-extract走css module,每個className都是獨(dú)一無二,那么通過globalStyle來對其子元素進(jìn)行樣式調(diào)整覆蓋完全是可行的。
- import { style, globalStyle } from '@vanilla-extract/css';
- export const parentClass = style({});
- globalStyle(`${parentClass} > div`, {
- color: 'red'
- });
- const Demo = () => (
- <div className={parentClass}>
- <Select/>
- </div>
- )
相比于 Styled-components 的優(yōu)點
- 零運(yùn)行時;
- 樣式開發(fā)走Typescript安全類型;
- style設(shè)計職責(zé)分離;(當(dāng)然,styled-components也是可以的,只是完全取決于看開發(fā)者)
- ...
總結(jié)
目前了解下來,vanilla-extract是一個總體還不錯的css in js庫,雖然目前使用率比較低,但是后續(xù)廠商平臺項目會考慮在一些地方使用看看效果(畢竟不會增大js體積)。