css-render,另一种 CSS-in-JS 的方式

386 阅读2分钟

(向大家介绍一个我写的另类 css-in-js 库,顺便求一些建议、星星或者 PR 🤣)

css-render@github

先说一下它解决了什么问题。

在创建一个 UI 组库的时候,我遇到了几个问题:

  1. 某个特定组件,需要内置的不同颜色过多,同样的逻辑重复很多遍,打包出来的样式即使 gzip 之后体积依然在 10KB 左右,不压缩体积完全不能看。如果能在浏览器动态生成,能节省很多打包体积(用 CPU 时间换带宽,在值得的时候)。
  2. 重复的逻辑是用 Sass 实现的,没有办法做到在浏览器端接受某个用户输入的颜色,即使这段逻辑已经用 Sass 实现了,除非我在浏览器解析 Sass,但是那样体积又太大了。 于是我开始着手解决这个问题,核心有几个需求:
    1. 需要能在浏览器端(动态,可接受用户输入)生成 CSS 并挂载。 自身需要够小,不能节省的体积比引入的体积还要大。
    2. @keyframes, ::before, :hover 等选择器友好,光靠 inline 不够用,因为动画可能也是元素样式的一部分。
    3. 尽量拥有较高的表达能力,能让一部分 Sass 代码以比较低的成本迁入。 能在 Node.js 端渲染就更好了。

所以有了开头那个库:它不是为了取代其他的样式方案,而是试图和它们共存,弥补其中的一些不足。

下面放几个例子:

基本例子

// 最简单的例子
import CSSRender from 'css-render'

const {
  c
} = CSSRender()

const style = c('body', {
  margin: 0,
  backgroundColor: 'white'
}, [
  c('&.dark', {
    backgroundColor: 'black'
  }),
  c('.container', {
    width: '100%'
  })
])

/** 作为字符串使用 */
console.log(style.render())
/**
 * 或者挂载于 document.head,
 * 下面两行只在浏览器工作,不要在 node.js 中调用
 */
style.mount()
// ...
style.unmount()

输出

body {
  margin: 0;
  background-color: white;
}

body.dark {
  background-color: black;
}

body .container {
  width: 100%;
}
  1. 一定程度的动态能力
const style = c('.button', ({
  props
}) => ({
  color: props.color
}))

console.log(style.render({ color: 'red' }))

console.log()

console.log(style.render({ color: 'blue' }))

输出

.button {
  color: red;
}

.button {
  color: blue;
}
  1. 对于不能内联的样式友好
console.log(c('@keyframes my-animation', {
  from: {
    color: 'white'
  },
  to: {
    color: 'black'
  }
}).render())

输出

@keyframes my-animation {
  from {
    color: white;
  }
  to {
    color: black;
  }
}
  1. 一定程度上能模仿 mixin 的效果(比如常见的 BEM 写法)
import CSSRender from 'css-render'
import CSSRenderBEMPlugin from '@css-render/plugin-bem'
/**
 * common js:
 * const { CSSRender } = require('css-render')
 * const { plugin: CSSRenderBEMPlugin } = require('@css-render/plugin-bem')
 */

const cssr = CSSRender()
const plugin = CSSRenderBEMPlugin({
  blockPrefix: '.c-'
})
cssr.use(plugin)
const {
  cB, cE, cM
} = plugin

const style = cB(
  'container',
  [
    cE(
      'left, right', 
      {
        width: '50%'
      }
    ),
    cM(
      'dark', 
      [
        cE(
          'left, right',
          {
            backgroundColor: 'black'
          }
        )
      ]
    )
  ]
)

/** use it as string */
console.log(style.render())

输出

.c-container .c-container__left, .c-container .c-container__right {
  width: 50%;
}

.c-container.c-container--dark .c-container__left, .c-container.c-container--dark .c-container__right {
  background-color: black;
}

如果想了解更多的用法,请转到仓库css-render@github

感谢大家的阅读,鞠躬。