我开源了一个css-in-js库:class-css

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样式对象转样式表文本的库。

代码仓库

class-css

相关推荐
Rattenking4 分钟前
node - npm常用命令和package.json说明
前端·npm·json
Easonmax5 分钟前
【HTML5】html5开篇基础(1)
前端·html·html5
For. tomorrow8 分钟前
Vue3中el-table组件实现分页,多选以及回显
前端·vue.js·elementui
布瑞泽的童话35 分钟前
无需切换平台?TuneFree如何搜罗所有你爱的音乐
前端·vue.js·后端·开源
白鹭凡1 小时前
react 甘特图之旅
前端·react.js·甘特图
2401_862886781 小时前
蓝禾,汤臣倍健,三七互娱,得物,顺丰,快手,游卡,oppo,康冠科技,途游游戏,埃科光电25秋招内推
前端·c++·python·算法·游戏
书中自有妍如玉1 小时前
layui时间选择器选择周 日月季度年
前端·javascript·layui
Riesenzahn1 小时前
canvas生成图片有没有跨域问题?如果有如何解决?
前端·javascript
f8979070701 小时前
layui 可以使点击图片放大
前端·javascript·layui