深圳幻海软件技术有限公司 欢迎您!

CSS in JS 新秀:Vanilla-Extract 浅析

2023-02-28

本文转载自微信公众号「Tecvan」,作者江江。转载本文请联系Tecvan公众号。前言2021年,vanilla-extract作为黑马登顶了css-in-js满意度榜首(虽然使用率仅为1%),号称是一个类型安全、高度兼容TS场景的库,国内相关讨论还很少,稍微看了一下还挺好用的。介绍官方文档:htt

本文转载自微信公众号「Tecvan」,作者江江。转载本文请联系Tecvan公众号。

前言

2021年,vanilla-extract 作为黑马登顶了 css-in-js 满意度榜首(虽然使用率仅为1%),号称是一个类型安全、高度兼容 TS 场景的库,国内相关讨论还很少,稍微看了一下还挺好用的。

介绍

官方文档:https://vanilla-extract.style/documentation/

打开vanilla-extract官网文档,里面已经罗列了他的那些优点。作为一个如果使用 css-in-js 会首选styled-components的我来说,比较关注的点主要是:

  • All styles generated at build time
  • Type-safe styles via CSSType.

这两点说白了就是,零运行时且支持typescript。

零运行时:vanilla-extract会在编译时期,编译出 css modules值和css内容,不需要带任何运行时内容到生产环境,相对来说运行速度更高,产物体积更小;

typescript:支持typescript类型检查,CSS安全;

目前,业界大多数相关竞品如 styled-components就是一个运行时方案且基于标签模板进行书写,主要基于stylis解析器解析,如果频繁更新props还会造成style标签大量插入到head里。

安装

Webpack 环境下,需要同时安装 @vanilla-extract/css 与 @vanilla-extract/webpack-plugin 插件:

yarn add @vanilla-extract/css @vanilla-extract/webpack-plugin 
  • 1.

安装完成后,修改 Webpack 配置:

const { VanillaExtractPlugin } = require('@vanilla-extract/webpack-plugin'
 
module.exports = { 
  entry: './src/index.js'
  // .... 
  plugins: [new VanillaExtractPlugin()] 
}; 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

这里可以看见,当我们使用 vanilla-extract 时,需要安装两个库:

  • @vanilla-extract/css:开发核心库,基于该库进行项目业务样式开发;
  • @vanilla-extract/webpack-plugin:webpack插件。前面也提及了vanilla-extract是一个零运行的库,主要是通过该插件处理。
    • 通过增加webpack配置项,对相关后缀文件使用自定义的@vanilla-extract/webpack-plugi/loader进行处理。
    • 在其内部通过eval库在编译时先执行得到className结果,避免在运行时执行过程;
    • 再移除@vanilla-extract/css库,使其不会影响到生产环境下js包体大小;

构建样式API

这里只会对vanilla-extract比较核心的构建样式相关几个API提及,其他API可以直接前往官网查看。

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; 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.

这个简单的demo我相信,看几下就已经明白是怎么使用了。但是需要理解的地方是,为了提高可维护性,「每个样式块只能针对某个元素(或者说是使用这个样式块的元素)」。那么在上述代码里的selectors而言,「其目标必须是」**&**(也就是自身元素)而不能是其他元素。例如:`${parentClass} &`是OK的,但是`& div`是不允许的。这样的设计,我觉得更是一种职责分离吧,每个样式块都针对某个元素,那么对于项目而言,样式的可维护性就大大提高了,相比于其他css in js(styled-components)就不容易出现样式冗余的问题。对于一些特殊情况,比如:在写styled-components我们会利用其包裹arco组件(或是其他组件),然后对其内部元素样式进行覆写或是新增。

const StyledSelect = styled(Select)` 
    div { 
        color: red; 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

那么在vanilla-extract样式块里是不能直接做到的,因为每个样式块都是针对某个元素,是不能直接通过该样式块,直接对其兄弟元素、子元素进行样式调整。但是,对于这种情况,是可以使用另一个APIglobalStyle进行开发。

globalStyle

顾名思义,就是全局样式API。但是因为本身vanilla-extract走css module,每个className都是独一无二,那么通过globalStyle来对其子元素进行样式调整覆盖完全是可行的。

import { style, globalStyle } from '@vanilla-extract/css'
 
export const parentClass = style({}); 
 
globalStyle(`${parentClass} > div`, { 
  color: 'red' 
}); 
 
const Demo = () => ( 
    <div className={parentClass}> 
        <Select/> 
    </div> 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

 

相比于 Styled-components 的优点

  • 零运行时;
  • 样式开发走Typescript安全类型;
  • style设计职责分离;(当然,styled-components也是可以的,只是完全取决于看开发者)
  • ...

总结

 

目前了解下来,vanilla-extract是一个总体还不错的css in js库,虽然目前使用率比较低,但是后续厂商平台项目会考虑在一些地方使用看看效果(毕竟不会增大js体积)。