@classmatejs/react

中文文档
0.3.0发布于 2026/06/28MITTypeScriptESM + CJS近 30 天下载 2.3万

一个反应工具可以分出类名逻辑,创建变体并管理样式.

此软件包已被标记为弃用

Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.

中文使用文档
文档目录23 节
  1. # # 转换这个 # # Form this #
  2. 进
  3. 特征
  4. 目录
  5. 开始
  6. 基础
  7. 使用道具
  8. 前置输入道具 $
  9. 创建变量
  10. Typescript:具有第二种类型参数的分离基道具和变体
  11. 扩展
  12. 变形
  13. 添加 CSS 样式
  14. 使用 React 组件 - useClassmate
  15. 添加逻辑头
  16. 食谱 cm.extend
  17. 从变量扩展
  18. 道具的自动推断类型
  19. 扩展其他 lib 组件 / any 作为输入
  20. 常见JS
  21. 尾风合并
  22. 即将
  23. 灵感

@ classmatejs/ 反应

一个管理 React 组件类名,变体和样式的工具.

# # 转换这个 # # Form this #

const SomeButton = ({ isLoading, ...props }) => {
  const activeClass = isLoading
    ? "bg-blue-400 text-white"
    : "bg-blue-800 text-blue-200";

  return (
    <button
      {...props}
      className={`transition-all mt-5 border-1 md:text-lg text-normal ${someConfig.transitionDurationEaseClass} ${activeClass} ${
        props.className || ""
      }`}
    >
      {props.children}
    </button>
  );
};

进

const SomeButton = cm.button`
  text-normal
  md:text-lg
  mt-5
  border-1
  transition-all
  ${someConfig.transitionDurationEaseClass}
  ${({ $isLoading }) => $isLoading && "opacity-90 pointer-events-none"}
`;

特征

  • 以名称为重点的类别组件
  • 备选案文
  • 扩展组件
  • 变形组件
  • 动态样式
  • 类型脚本支持
  • 试用了安全部门改革框架
  • 类名合并

目录

开始

确定您已经安装 反应 ( > 16.8.0) 输入 项目.

npm i @classmatejs/react
# or
yarn add @classmatejs/react

对于捆绑的应用程序,您也可以将 ClassmateJS 安装为 dev 依赖:

npm i -D @classmatejs/react
# or
yarn add -D @classmatejs/react

对解决的软件包/图书馆或部署使用常规依赖 node_modules 仅安装生产依赖的运行时间.

基础

通过调用创建组件 cm 带有标签名称和模板文字 绳子.

import cm from "@classmatejs/react";

const Container = cm.div`
  py-2
  px-5
  min-h-24
`;
// transforms to: <div className="py-2 px-5 min-h-24" />

使用道具

将道具传递到组件,并在模板字串和其中使用 组件道具验证.

// hey typescript
interface ButtonProps {
  $isActive?: boolean;
  $isLoading?: boolean;
}
const SomeButton = cm.button<ButtonProps>`
  text-lg
  mt-5
  ${({ $isActive }) => ($isActive ? "bg-blue-400 text-white" : "bg-blue-400 text-blue-200")}
  ${({ $isLoading }) => $isLoading && "opacity-90 pointer-events-none"}
`;
// transforms to <button className="text-lg mt-5 bg-blue-400 text-white opacity-90 pointer-events-none" />

前置输入道具 $

我们在道具上加注 $ 签名这很重要 将动态道具与我们传递给组件的道具区分开来.

这种模式还应避免与保留道具名称相冲突.

创建变量

通过将对象传递到 variants 密钥类似 简历。键应匹配 道具名称和值应该是返回字符串的函数。 你会的 也重新使用函数中的道具.

interface AlertProps {
  $severity: "info" | "warning" | "error";
  $isActive?: boolean;
}
const Alert = cm.div.variants<AlertProps>({
  // optional
  base: (p) => `
    ${p.isActive ? "custom-active" : "custom-inactive"}
    p-4
    rounded-md
  `,
  // required
  variants: {
    $severity: {
      warning: "bg-yellow-100 text-yellow-800",
      info: (p) =>
        `bg-blue-100 text-blue-800 ${p.$isActive ? "shadow-lg" : ""}`,
      error: (p) =>
        `bg-red-100 text-red-800 ${p.$isActive ? "ring ring-red-500" : ""}`,
    },
  },
  // optional - used if no variant was found
  defaultVariant: {
    $severity: "info",
  },
});

export default () => <Alert $severity="info" $isActive />;
// outputs: <div className="custom-active p-4 rounded-md bg-blue-100 text-blue-800 shadow-lg" />

Typescript:具有第二种类型参数的分离基道具和变体

从上面看,我们也通过了 AlertProps 以各种变体为主 类型。 如果你想从变体中分离出基本道具,你可以通过一个 转到第二个类型 variants 函数,只允许在 这些变体.

interface AlertProps {
  $isActive?: boolean;
}
interface AlertVariants {
  $severity: "info" | "warning" | "error";
}
const Alert = cm.div.variants<AlertProps, AlertVariants>({
  base: `p-4 rounded-md`,
  variants: {
    // in here there are only the keys from AlertVariants available
    $severity: {
      // you can use the props from AlertProps here again
      warning: "bg-yellow-100 text-yellow-800",
      info: (p) =>
        `bg-blue-100 text-blue-800 ${p.$isActive ? "shadow-lg" : ""}`,
      error: (p) =>
        `bg-red-100 text-red-800 ${p.$isActive ? "ring ring-red-500" : ""}`,
    },
  },
  // optional - used if no variant was found
  defaultVariant: {
    $severity: "info",
  },
});

扩展

直接通过传递组件和标签名来扩展组件.

import MyOtherComponent from "./MyOtherComponent"; // () => <button className="text-lg mt-5" />
import cm from "@classmatejs/react";

const Container = cm.extend(MyOtherComponent)`
  py-2
  px-5
  min-h-24
`;
// transforms to: <button className="text-lg mt-5 py-2 px-5 min-h-24" />

变形

变形使已经存在的同龄人成分成为另一个内在元素. 它保持原有的阶级逻辑 $ 道具, 但类型脚本验证道具 对新元素.

import cm from "@classmatejs/react";

const MyButton = cm.button<{ $toggleCta?: boolean }>`
  absolute
  min-w-300
  ${({ $toggleCta }) => ($toggleCta ? "bg-red-500" : "bg-blue-500")}
`;

export default () => <MyButton $_as="span" $toggleCta />;
// outputs: <span className="absolute min-w-300 bg-red-500" />

需要重用已转换组件时使用构建器 API, 或者添加更多 带有标记模板的类.

const SpanButton = cm.transform(MyButton).span;
const TextSpanButton = cm.transform(MyButton).span`
  text-neutral
  tracking-wide
`;

export default () => <TextSpanButton $toggleCta />;
// outputs: <span className="absolute min-w-300 bg-red-500 text-neutral tracking-wide" />

变形也与变相和扩展的同学组件相配合.

const VariantDiv = cm.div.variants<{ $size: "sm" | "md" }>({
  base: "absolute min-w-300",
  variants: {
    $size: {
      sm: "w-10 h-10",
      md: "w-20 h-20",
    },
  },
  defaultVariants: {
    $size: "md",
  },
});

const Main = cm.transform(VariantDiv).main`
  text-neutral
  tracking-wide
`;

export default () => <Main $size="sm" role="main" />;
// outputs: <main className="absolute min-w-300 w-10 h-10 text-neutral tracking-wide" role="main" />

cm.transform 只接受 v1 中的同学组件 。 常规反应 组件不能变换,因为同学不知道在哪里 课程随附.

添加 CSS 样式

您可以在模板文字字符串中使用 CSS 样式 style 函数. 此函数取一个带有 CSS 属性的对象并返回字符串 。 我们可以 使用之前的道具.

// Base:
const StyledButton = cm.button<{ $isDisabled: boolean }>`
  text-blue
  ${(p) =>
  p.style({
    boxShadow: "0 0 5px rgba(0, 0, 0, 0.1)",
    cursor: p.$isDisabled ? "not-allowed" : "pointer",
  })}
`;
export default () => <StyledButton $isDisabled />;
// outputs: <button className="text-blue" style="box-shadow: 0 0 5px rgba(0, 0, 0, 0.1); cursor: not-allowed;" />
// Extended:
const BaseButton = cm.button<{ $isActive?: boolean }>`
  ${(p) =>
  p.style({
    backgroundColor: p.$isActive ? "green" : "red",
  })}
`;
const ExtendedButton = cm.extend(BaseButton)<{ $isLoading?: boolean }>`
  ${(p) =>
  p.style({
    opacity: p.$isLoading ? 0.5 : 1,
    pointerEvents: p.$isLoading ? "none" : "auto",
  })}
`;
export default () => <ExtendedButton $isActive $isLoading />;
// outputs: <button className="bg-red" style="opacity: 0.5; pointer-events: none;" />

使用 React 组件 - useClassmate

当您需要在另一个 React 组件内创建同学组件时 (例如,当配置依赖于只运行时间值时),将 工厂 useClassmate。这可以记住结果,避免产生 每一个渲染器都有全新的组件.

import cm, { useClassmate } from "@classmatejs/react";

const WorkoutDay = ({ status }: { status: "completed" | "pending" }) => {
  const StyledDay = useClassmate(
    () =>
      cm.div.variants({
        base: "rounded border p-4 text-sm",
        variants: {
          $status: {
            completed: "border-green-400 bg-green-50",
            pending: "border-yellow-400 bg-yellow-50",
          },
        },
      }),
    [status], // recompute when dependencies change
  );

  return <StyledDay $status={status}>Workout details</StyledDay>;
};

依赖数组的行为 React.useMemo把工厂的东西都递过来 如果您期望组件在这些值变化时更新, 则关闭 .

添加逻辑头

使用 .logic() 在您的课前运行任意 JavaScript 一次 名称或变体是计算出来的。 返回值是浅浮回 道具,这样你就能得到 $ 道具, DOM 属性, 或者其他什么 组件需要没有额外的钩子.

type DayStatus = "completed" | "pending"

interface WorkoutProps {
  workouts: unknown[]
  allResolved: boolean
  hasCompleted: boolean
  hasSkipped: boolean
  $status?: DayStatus
}

const WorkoutDay = cm.div
  .logic<WorkoutProps>((props) => {
    const status = deriveDayStatus(props)
    return {
      $status: status,
      ["data-status"]: status,
    }
  })
  .variants<WorkoutProps, { $status: DayStatus }>({
    base: "rounded border p-4",
    variants: {
      $status: {
        completed: "bg-green-50 border-green-400",
        pending: "bg-white border-slate-200",
      },
    },
  })

// Consumers only pass raw workout data – the logic header derives $status for you.
<WorkoutDay workouts={workouts} allResolved hasCompleted hasSkipped={false} />

返回值从 .logic() 因此以后的逻辑调用可以 引用早先的结果或推翻它们.

食谱 cm.extend

与 cm.extend中,您可以在任何基底 React 组件上再加新样式 甚至支持额外的道具。 这样就容易再使用 组件变化而不重复逻辑.

import { ArrowBigDown } from "lucide-react";
import cm from "@classmatejs/react";

const StyledLucideArrow = cm.extend(ArrowBigDown)`
  md:-right-4.5
  right-1
  slide-in-r-20
`;

// ts: we can pass only props which are accessible on a `lucid-react` Component
export default () => <StyledLucideArrow stroke="3" />;

⚠️由于扩展第三方组件而出现问题,见: 扩展其他 lib 组件

现在,我们可以定义一个基础组件, 扩展它的附加样式和 类,并传递属性。 你可以把类型传给 extend 函数改为 获得自动补全和类型检查.

import cm from "@classmatejs/react";

interface StyledSliderItemBaseProps {
  $active: boolean;
}
const StyledSliderItemBase = cm.button<StyledSliderItemBaseProps>`
  absolute
  h-full
  w-full
  left-0
  top-0
  ${(p) => (p.$active ? "animate-in fade-in" : "animate-out fade-out")}
`;

interface NewStyledSliderItemProps extends StyledSliderItemBaseProps {
  $secondBool: boolean;
}
const NewStyledSliderItemWithNewProps = cm.extend(
  StyledSliderItemBase,
)<NewStyledSliderItemProps>`
  rounded-lg
  text-lg
  ${(p) => (p.$active ? "bg-blue" : "bg-red")}
  ${(p) => (p.$secondBool ? "text-underline" : "some-class-here")}
`;

export default () => (
  <NewStyledSliderItemWithNewProps $active $secondBool={false} />
);
// outputs: <button className="absolute h-full w-full left-0 top-0 animate-in fade-in rounded-lg text-lg bg-blue" />

从变量扩展

interface ButtonProps extends InputHTMLAttributes<HTMLInputElement> {
  $severity: "info" | "warning" | "error";
  $isActive?: boolean;
}

const Alert = cm.input.variants<ButtonProps>({
  base: "p-4",
  variants: {
    $severity: {
      info: (p) =>
        `bg-blue-100 text-blue-800 ${p.$isActive ? "shadow-lg" : ""}`,
    },
  },
});

const ExtendedButton = cm.extend(Alert)<{ $test: boolean }>`
  ${(p) => (p.$test ? "bg-green-100 text-green-800" : "")}
`;

export default () => <ExtendedButton $severity="info" $test />;
// outputs: <input className="p-4 bg-blue-100 text-blue-800 shadow-lg bg-green-100 text-green-800" />

道具的自动推断类型

通过通过组件,我们可以验证组件以接受与标签相关的标记 道具来. 如果你想依靠道具来获取特定元素,这个就有用了 没有 $ 前缀.

// if you pass rc component it's types are validated
const ExtendedButton = cm.extend(cm.button``)`
  some-class
  ${(p) => (p.type === "submit" ? "font-normal" : "font-bold")}
`;

// infers the type of the input element + add new props
const MyInput = ({ ...props }: HTMLAttributes<HTMLInputElement>) => (
  <input {...props} />
);
const StyledDiv = cm.extend(MyInput)<{ $trigger?: boolean }>`
  bg-white
  ${(p) => (p.$trigger ? "!border-error" : "")}
  ${(p) => (p.type === "submit" ? "font-normal" : "font-bold")}
`;

扩展其他 lib 组件 / any 作为输入

不幸的是,我们无法直接推断 类型的组件,如果它是 any 或打字松动。 但我们可以用中间步骤 将类型传递到 extend 函数.

import { ComponentProps } from 'react'
import { MapContainer } from 'react-leaflet'
import { Field, FieldConfig } from 'formik'
import cm, { CmBaseComponent } from 'react-classmate'

// we need to cast the type to ComponentProps
type StyledMapContainerType = ComponentProps<typeof MapContainer>
const StyledMapContainer: CmBaseComponent<StyledMapContainerType> = cm.extend(MapContainer)`
  absolute
  h-full
  w-full
  text-white
  outline-0
`

export const Component = () => <StyledMapContainer bounds={...} />

// or with Formik

import { Field, FieldConfig } from 'formik'

type FieldComponentProps = ComponentProps<'input'> & FieldConfig
const FieldComponent = ({ ...props }: FieldComponentProps) => <Field {...props} />

const StyledField = cm.extend(FieldComponent)<{ $error: boolean }>`
  theme-form-field
  w-full
  ....
  ${p => (p.$error ? '!border-error' : '')}
`

export const Component = () => <StyledField placeholder="placeholder" as="select" name="name" $error />

常见JS

如果您使用CommonJS, 您可以这样导入库 :

const cm = require("@classmatejs/react").default;

// or

const { default: cm } = require("@classmatejs/react");

尾风合并

反应类同位素使用 后风相接 在罩子下合并类名. 最后一个班的名字总能赢 所以你 可以使用它来覆盖类.

即将

  • 错误/ 排除出错: 由 ref.current( use Ref) 设置的类名将被覆盖 一旦组件重新投递
    • 在文档中至少需要一条小文章
  • cm.raw() 财务报告和审定财务报表 cm.raw.variants() 仅用于使用 rc 类名的语法 (输出为字符串)
  • 备选案文 cm.extend
  • 名称为 commonJS 的 lib 导入( 目前仅是) .default) -- -- 意味着我们需要 删除 ts 文件内命名的导出以不重复 IDE 导入 建议: -- -- 更改构建后脚本以删除命名的 Esm 导出
  • 纳入更多的测试、侧重于安全部门改革和反应的基准
  • 高级 IDE 集成
    • 在徘徊时显示生成的默认类
    • 从已使用的 libs 执行自动补全和工具提示

灵感

版本与发布记录

@classmatejs/react 共发布 10 个版本,首次发布于 2025/11/25;过去 12 个月发布了 10 个版本。

版本发布日期距今
0.3.0latest2026/06/284 个月前
0.2.12026/02/079 个月前
0.1.12026/02/079 个月前
0.1.02025/11/2911 个月前
0.0.62025/11/2711 个月前
0.0.52025/11/2711 个月前

关于 @classmatejs/react 的常见问题

@classmatejs/react 是什么?

一个反应工具可以分出类名逻辑,创建变体并管理样式.注意:该包已被作者标记为弃用——Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.

如何安装 @classmatejs/react?

在项目目录运行 npm install @classmatejs/react,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:react(>=16.8.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@classmatejs/react 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@classmatejs/react 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@classmatejs/react 可以商用吗?

@classmatejs/react 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@classmatejs/react 的下载量有多少?

根据 npm 官方下载统计,@classmatejs/react 在 2026/10/10 – 2026/11/08 这 30 天内被下载 23,257 次,日均约 775 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@classmatejs/react 还在维护吗?

最新版本 0.3.0 发布于 2026/06/28(4 个月前);至今共发布 10 个版本,其中过去 12 个月发布了 10 个;首次发布于 2025/11/25。作者已将其标记为弃用,新项目建议寻找替代方案。