Files
yx-react-template/CLAUDE.md
2026-09-08 14:07:42 +08:00

9.9 KiB
Raw Permalink Blame History

CLAUDE.md

本文件记录 Claude Code 在本仓库中开发、创建文件和修改代码时必须遵守的项目规范。规范以当前仓库的代码、配置和 README 为准;如配置发生变化,优先同步更新本文件。

项目概览

  • 技术栈:React 19、TypeScript、Vite 8、React Router 7、Tailwind CSS 4、Zustand。
  • 包管理与脚本:使用 Bun。
  • 代码检查与格式化:使用 Biome。
  • 提交前检查:Husky + lint-staged。
  • 运行环境:易班 H5 课程模板,包含 videoajax.js 封装、1920 x 1080 整体缩放、横屏访问提示和移动端 vconsole 调试。

常用命令

以 package.json 为准:

bun dev            # 启动开发服务器
bun run build      # 类型检查并构建生产版本
bun run preview    # 预览生产构建
bun run lint       # 代码检查
bun run lint:fix   # 自动修复代码问题

修改代码后,至少运行 bun run lint。如果改动影响路由、构建配置、页面结构、类型系统或模板能力,还应运行 bun run build。

代码风格

以 biome.json 为准:

  • 使用 Biome 进行 JS/TS/JSON 等文件的格式化与检查。
  • 使用 tab 缩进。
  • JavaScript/TypeScript 字符串使用双引号。
  • 导入整理交给 Biome 的 organizeImports。
  • 当前 CSS linter/formatter 关闭,不要擅自引入新的 CSS 格式化标准。
  • 写代码时匹配周围代码的命名、注释密度和组织方式,不为了“统一”改写无关文件。

文件创建与目录归属

新增文件前先判断职责归属,优先沿用现有目录结构:

  • src/components/ui/:无课程业务和全局状态依赖的基础 UI copied source。
  • src/components/common/:模板通用组件和适配器;其中 GuideDialog、GuideSpotlight 负责连接 Zustand、CourseCanvas 与交互内核。新增通用组件后,按需在 src/components/common/index.ts 维护导出。
  • src/components/business/:业务组合组件,不放通用基础能力。
  • src/course/canvas/:设计稿画布、缩放、viewport 和课程 Portal 根节点。
  • src/course/orientation/:横竖屏访问控制。
  • src/course/interactions/:由 CLI 复制的受控课程交互内核,不依赖具体页面、Store 或 common 适配器。
  • src/pages/<PageName>/:页面组件。页面目录入口使用 index.tsx,页面组件使用默认导出。
  • 模块内私有组件:仅本模块使用、其他模块用不到的,放到本模块目录的 components/ 子目录(没有就创建);被多个模块或组件复用才下沉到 src/components/common/(通用基础能力)或 business/(业务组合),按组件核心类型区分。该规则约束新代码,存量平铺在页面目录下的模块内组件不强制迁移。
  • src/router/index.tsx:集中维护路由配置。新增页面优先复用 lazyRoute 和 RouteFallback。
  • src/stores/:Zustand 状态管理。
  • src/utils/:通用工具、hooks 或平台封装。
  • src/types/:只放跨多个模块复用的全局共享类型。
  • src/styles/:全局样式和重置样式。

类型放置规则

组件、页面、store、API 的对外 props 与跨文件共享的 interface、type 放在 .types.ts;文件内私有的 helper 类型留在实现文件即可:

  • 组件类型放在同组件目录的 组件名.types.ts,例如 Button/Button.types.ts。
  • 页面类型如确实需要,放在页面目录的 PageName.types.ts。
  • store 类型统一放在 src/stores/types/*.types.ts,例如 src/stores/types/user.types.ts。
  • API 模块类型放在对应模块的 *.types.ts,例如 src/api/client.types.ts。
  • 只有跨多个页面、组件、store、utils 复用的全局共享类型,才放入 src/types/。
  • 不要把组件 props、store slice state 或组件内部布局类型放入 src/types/,避免全局类型目录变成杂物目录。

Tailwind、cn 与样式提取规则

项目已有 src/lib/cn.ts 中的 cn,用于合并 Tailwind class 并处理冲突(src/utils/tailwind.ts 只是 re-export)。使用时遵守以下边界。

默认策略

  • 短的静态 Tailwind class 可以直接写在 JSX 的 className 中。
  • 不为了“看起来统一”强行把所有 class 都包进 cn。
  • 不为了少量 class 新增多层封装、过度抽象或大量碎片常量。

什么时候使用 cn

在以下场景使用 cn:

  • 需要合并外部传入的 className。
  • 条件 class 需要和其他 class 合并。
  • 需要通过 tailwind-merge 解决 Tailwind 冲突合并。
  • 需要组合 CSS Module、固定 class、条件 class 或外部 className。

示例:

<div className={cn(styles.panel, active && "opacity-100", className)} />

什么时候不使用 cn

以下场景不要使用 cn:

<div className="flex items-center justify-center" />
<div className={active ? "h-2 w-8 bg-current" : "h-2 w-2 bg-current/20"} />

单个固定字符串,或在两个完整字符串之间二选一的三元且不需要再合并其他 class 时,直接写 className。

什么时候提取样式

只有在以下情况才提取 class:

  • Tailwind 字符串过长,导致 JSX 难读。
  • 同一组 class 在同一文件或多个相近组件中重复出现。
  • 条件组合已经让 cn(...) 变得很长。
  • 提取后能更清楚表达布局或业务意图。
  • Tailwind 直接实现过于麻烦、复杂或冗余,此时优先考虑 CSS Module、已有组件 API 或更简单的结构。

提取位置:

  • 组件/页面私有样式常量优先放在当前文件内。
  • 同目录多个文件共享时,可放同目录局部文件。
  • 跨组件复用的样式能力,再考虑沉淀为通用组件、工具或 CSS Module。

组件与 CSS 类的边界

「何时提取 class」见上一节;本节只解决复用一段 UI 时该抽组件还是抽 CSS 类。

组件/页面文件超过 500 行时,先审视是否该拆分(这是触发思考的信号,不是强制拆分线);拆出的子组件按「文件创建与目录归属」放置。

重复出现时优先抽组件,CSS 类是兜底,但避免「为整洁而抽」。抽组件必须同时满足:

  • 同一组「标记 + 样式 + 行为」在 2 处以上真实重复(第一次出现不抽)。
  • 组件有清晰可命名的语义,能一句话说清它是什么。
  • 复用的是整体而非几个 class;props 少且稳定(一般 ≤3~4 个)。

以下情况改用 CSS 类或局部常量,而不是抽组件:

  • 只复用样式、不复用标记/行为,内容由数据或富文本决定。
  • 仅同一文件内的小重复。
  • 给第三方/框架等无法插入组件的位置加样式。
  • Tailwind 表达不了或 class 串过长,但元素本身不够独立语义。

反模式(不要抽):只被用一次仅为缩短 JSX;组件 props 塞满 className/style 覆盖;名字说不清职责(CardTopSection、Wrapper 之类)。

动画使用规则

项目使用 CSS/Tailwind 和 GSAP 时遵守以下边界:

  • 普通状态过渡、hover、active、简单淡入淡出及单元素入场默认使用 CSS/Tailwind。
  • 只有动画需要时间轴编排、暂停/继续/倒放、运行时动态计算、音视频同步或复杂路径时,才使用 GSAP。
  • 不因项目已经安装 GSAP 而重写现有 CSS 动画,也不为简单动画建立 GSAP preset 或通用封装。
  • CSS 与 GSAP 不要在同一交互周期内控制同一元素的同一属性,尤其是 transform 和 opacity。
  • GSAP 动画通过 src/utils/motion.ts 使用 useGSAP 管理生命周期,并处理 prefers-reduced-motion 降级。
  • 不使用 GSAP 修改 CourseCanvas、画布 portal 根节点或 dnd-kit 正在控制的 transform。

导入与导出

  • @ 指向 src,新增代码优先使用 @ 避免过长相对路径。
  • 同目录内局部文件可以继续使用相对路径。
  • 保持现有 barrel export 风格:通用组件、utils、stores 等需要对外复用时,再维护对应 index.ts。
  • 不要为了导出方便把不该公开的内部实现暴露出去。

路由与页面

  • 路由集中在 src/router/index.tsx。
  • 新页面优先使用目录入口懒加载,例如 lazyRoute(() => import("../pages/About"))。
  • 页面组件使用默认导出。
  • 新增路由时同步考虑 HydrateFallback: RouteFallback。
  • 不要绕开现有 hash router 和懒加载模式,除非用户明确要求调整路由架构。

模板能力约束

开发时要尊重模板已有能力:

  • 页面按 1920 x 1080 设计稿开发,整体缩放由 src/course/canvas 的 CourseCanvas 组件承担。
  • 横屏访问提示由 src/course/orientation 的 OrientationGuard 组件承担。
  • 易班能力通过 src/utils/yiban.ts 和 public/videoajax/ 相关脚本封装。
  • 移动端调试使用 src/utils/vconsole.ts。
  • 图片按钮由 src/components/common/ImageButton 提供;视频播放与视频弹层由 src/components/common/Video 提供。
  • 拖拽交互由 src/course/interactions/drag-drop 提供(基于 dnd-kit)。
  • 音频能力由 src/utils/audio 提供(GameAudioProvider / useGameAudio / AudioManager)。
  • GSAP 复杂动画通过 src/utils/motion.ts 统一引入,边界见「动画使用规则」。
  • 新增通用组件时,同步考虑是否需要在 src/pages/Components/index.tsx 增加示例。

Claude Code 工作准则

  • 编辑前先阅读相关文件,查找现有模式和可复用实现。
  • 优先复用已有组件、工具、store 和样式方案。
  • 不随意替换技术栈、引入新依赖或新增全局抽象。
  • 新建文件时先判断目录归属、类型放置位置、是否需要导出、是否需要示例页。
  • 修改配置、路由、构建、全局样式等影响范围较大的内容前,应确认需求明确。
  • 不绕过 lint/build 结果;如果校验失败,必须如实报告失败命令、错误摘要和影响范围。