class-css
是一个css-in-js库,可以通过编写 js 样式对象的形式享受伪类、选择器等功能。
快速开始
shell
npm install class-css
默认使用方式:
tsx
import { css } from 'class-css';
export default () => {
return (
<div className={css({ color: 'red' })}>
一段文字
</div>
)
};
或者创建一个新的实例并调用其方法:
tsx
import { createClassCss } from 'class-css';
const { css, keyframes } = createClassCss({
key: 'tiger-ui'
});
export default () => {
return (
<div className={css({ color: 'red' })}>
一段文字
</div>
)
};
需要注意的是,具有相同key的实例是同一个实例。(默认key为"css")
支持 css、keyframe、'&'
js 生成 css
样式。
tsx
import { css } from 'class-css';
export default () => {
return (
<div
className={css({
color: 'red'
})}
>
一段文字
</div>
);
}
js 生成 keyframes
。
tsx
import { css, keyframes } from 'class-css';
export default () => {
const appear = keyframes({
from: {
opacity: 0,
},
to: {
opacity: 1,
},
});
return (
<div
className={css({
animation: `${appear} 1s forwards`,
})}
>
一段文字
</div>
);
};
强大的'&',可实现伪类、伪元素、选择器等复杂功能。
tsx
import { css, keyframes } from 'class-css';
const myBtn = css({
background: 'white',
transition: 'all .3s',
cursor: 'pointer',
border: '1px solid #e8e8e8',
padding: '4px 12px',
borderRadius: 2,
'&:hover': {
background: 'whitesmoke'
},
'&:active': {
background: 'gray'
},
'& + &': {
marginLeft: 8
},
'& span': {
color: 'red'
}
})
export default () => {
return (
<div>
<button className={myBtn}>按钮</button>
<button className={myBtn}>按钮</button>
<button className={myBtn}>
<span>按钮</span>
</button>
</div>
);
};
为什么要自己实现 css-in-js 类库?
我之前使用的是 @emotion/css
,如果一个页面大量调用其css
方法,它会在短时内不断创建大量的style标签。
为了解决这个问题,我编写了class-css
,确保每个实例在全局仅一个style标签,同时设计了缓存方案避免大量重复计算。

测试代码:
tsx
import { css as emotionCss } from "@emotion/css";
import { css } from "class-css";
import { useState } from "react";
function App() {
const [className, setClassName] = useState("");
const [className2, setClassName2] = useState("");
return (
<div>
<div style={{ display: "flex", gap: 8, paddingBottom: 8 }}>
{["green", "yellow", "blue", "red"].map((color) => {
return (
<button
key={color}
onClick={() => {
setClassName(css({ color }));
setClassName2(emotionCss({ color }));
}}
>
{color}
</button>
);
})}
</div>
<div className={className}>@emotion/css</div>
<div className={className2}>class-css</div>
</div>
);
}
在项目中使用
可以用于任意支持打包的web应用框架。(后续会提供直接在原生html下开发css-in-js解决方法)
In React
tsx
import React from "react";
import { css } from "class-css";
export default () => {
return <div className={css({ color: "red" })}>一段文字</div>;
};
In Vue
tsx
<template>
<div :class="css({ color: 'red' })">一段文字</div>
</template>
<script setup>
import { css } from 'class-css';
</script>
class-css的实现原理
调用过程:
- 初始化实例。
- 调用实例的 css(styleObject) 方法。
- 对 styleObject 计算唯一hash。
- 对 styleObject 计算样式表文本 styleText。
- 将 hash、styleText 作为键值对的形式加入到缓存 cache 中。
- 检查缓存中不存在该 hash,则增量更新 styleText 到全局 style 标签。
- 返回唯一 className 类名。
简单来说,就是转化样式对象为样式文本,增量更新样式文本到style标签上,最后返回className。 (需要注意的是:相同的key对应同一个实例,包括缓存,对应的style标签等。)
性能优化
其中有几个耗时步骤:
- (大)样式对象计算 hash 值(支持自定义hash函数)
- (大)样式对象转为样式文本
- (小)缓存检测
- (中)style标签内容更新(何时更新、更新什么)
使用 css-in-js 类库有什么好处?
- 无需引入 css 样式文件
- 可以通过 js 方便控制样式,例如修改主题色
- 实现 style 不支持的功能,例如:伪选择器(:hover)等。
- 支持全局类名覆盖(使用hash配合计算唯一性)
一些额外的 monorepo 子库
- class-css-cache:处理缓存的库。
- class-css-stringify:处理JS样式对象转样式表文本的库。