React 前端
适用于每个 React 前端的规则。此文件包含了始终适用的原则;在开始相关主题的工作前,请先阅读匹配的参考资料。
| 任务 | 阅读 |
|---|---|
| 文件夹、层、切片、公共API、导入、桶文件(barrel files) | references/structure.md |
| FSD 深入:决策树、交叉导入、实体、资产、认证/API 放置、框架连接、迁移 | references/fsd/(索引在 structure.md 末尾) |
| 命名、文件大小、属性、设计系统、布局、常量 | references/code-style.md |
| 组件、Hook、副作用、记忆化、过渡、长列表、水合 | references/hooks-and-rendering.md |
| 瀑布请求、包体积、代码分割、预加载、重渲染成本 | references/performance.md |
| 服务器数据、查询、变更、加载器、搜索参数、Zustand | references/data-and-state.md |
| 表单、验证、表单错误 | references/forms.md |
| 语义、键盘、焦点、对比度、活动区域、无障碍测试 | references/accessibility.md(React 实现)+ frontend-accessibility 技能(完整规则、审计) |
| 单元测试、组件测试和端到端测试、覆盖率、API 模拟 | references/testing.md |
| 故事、Storybook 无障碍检查、视觉回归 | references/storybook.md |
| XSS、渲染 HTML/Markdown/LLM 输出、认证、CSRF、CSP、环境变量、登出 | references/security.md |
| Node 版本、包管理器、pre-commit + gitleaks、依赖审计 | references/tooling.md |
翻译、语言切换、<html lang> | references/i18n.md |
| 新建项目设置 | references/setup.md |
| 前端的容器镜像 | references/docker.md + docker-kubernetes 技能 |
技术栈
- React 19(≥ 19.2 以获得
useEffectEvent和<Activity>)· TypeScript strict - 渲染:Vite SPA 或 React Router 框架模式(SSR + 水合)。标记为 React Router 框架专属 的规则仅适用于此模式。无 Next.js、无 React Server Components、无 SWR。
- 路由:TanStack Router 或 React Router — 每个项目固定,从代码中检测,切勿混用。
- 服务器状态:TanStack Query · 客户端状态:Zustand
- 表单:React Hook Form + Zod 或 TanStack Form + Zod — 每个项目固定
- 验证:每个边界处使用 Zod(API 响应、表单、搜索参数、环境变量、存储、
postMessage) - 工具链:Biome(lint + format)、Steiger(FSD 规则)、Vitest + Testing Library、Playwright、Storybook
- 无 React Compiler → 记忆化手动进行,但仅限测量之后。
结构 — Feature-Sliced Design
层级 app → pages → widgets → features → entities → shared;仅向下导入;禁止同层切片之间的导入;每个切片仅通过其 index.ts 文件导入,且显式命名导出(无 export *)。shared/ui 和 shared/lib 每个组件/库对应一个索引文件。从 app、pages、shared 开始;不要创建空的层级文件夹。代码保留在页面中,直到满足提取规则 — 即当前在多个地方使用、有独立于任何消费者的变更理由、以及有集中的职责;三者都满足时方可提取,在此之前允许重复。不鼓励使用 widgets:仅当某组合块在多个页面中复用且不能放在页面中时才使用(现有 widget 继续有效)。详情:references/structure.md,上游规则手册 references/fsd/rules.md。
- 一个文件一个组件,一个文件一个 Hook,每个组件约 150 行;组件以其切片名称作为前缀(
references/code-style.md)。 - 优先使用设计系统:使用来自
shared/ui/的自有组件;切勿在shared/ui/外部导入shared/ui/primitives/(UI 库 CLI 输出,切勿编辑)。在检查设计系统组件、属性和标记能实现什么之前,不要新增 CSS。
组件和 Hook — 始终如此
- 切勿在另一个组件内部定义组件 — 这样会导致每次渲染时重新挂载。
- 派生,而非同步:可从 props/state 计算得出的值在渲染期间计算。不要使用
useEffect将 props 或 state 复制到 state 中。使用key重置。 - 用户操作在事件处理函数中执行,而非在由状态标志触发的 effect 中。
- Effect 仅用于与外部系统同步,具有狭窄的基本类型依赖,每个 effect 只关注一件事,并提供清理函数。切勿禁用 exhaustive-deps 规则。
- 当新值依赖于旧值时,使用
setState(prev => …);对于昂贵的初始值,使用useState(() => expensive())。 - 切勿修改 props 或 state:使用
toSorted()、toReversed()、toSpliced()、with()、展开运算符。 - 使用
{count > 0 ? <X /> : null}— 切勿在左侧为数字时使用&&。 - 不要在
useEffect中使用fetch;服务器数据来自 TanStack Query 或路由加载器。 memo/useMemo/useCallback仅在性能分析之后或需要引用稳定性时使用。- 错误绝不能静默处理:无空的
catch,无.catch(() => {})。每个路由都有一个错误边界;每个查询都有加载、错误和空 UI 状态。
数据和状态 — 始终如此
- 请求函数 → Hook → 组件。 HTTP 仅通过
shared/api/中的客户端进行;不要在 Hook 或组件中导入 HTTP 客户端。 - 服务器数据仅存在于 TanStack Query 中,通过包含
queryOptions的查询键工厂管理;在请求函数中使用 Zod 解析响应。 - 普通资源访问、CRUD 和查询键工厂位于
shared/api(或单独使用它们的页面/特性中);entities/<x>/api仅用于包含领域规则或该实体映射的请求。 - 变更会失效(或更新)所有受影响的查询。
- URL 状态(过滤器、排序、分页、标签)位于经过类型化和 Zod 验证的搜索参数中 — 切勿手动从
window.location解析。 - Zustand 仅用于共享的客户端状态;始终使用选择器;持久化存储使用
version、migrate和partialize,且不持有敏感数据。 - 在路由加载器中并行加载路由数据(
queryClient.query({ ...options, staleTime: 'static' }),原ensureQueryData),而非在嵌套组件中依次加载。
可访问性 — 内建,从不事后添加
目标:WCAG 2.2 AA。它是每个组件和页面验收标准的一部分。frontend-accessibility 技能包含完整的规则和审计流程 — 每次 UI 变更时都应加载它。
- 优先使用语义 HTML:使用
<button>表示操作、<a>/<Link>表示导航、每个字段使用<label>、地标元素、一个<h1>以及有序的标题层级。仅在没有原生元素匹配时才使用 ARIA。 - 键盘:所有内容均可通过键盘到达和操作;无正值的
tabIndex;无键盘陷阱。 - 可见焦点:每个交互元素上可见(
:focus-visible,对比度 ≥ 3:1)。 - 焦点管理:路由变化时设置页面标题并将焦点移至
<h1>或<main>;对话框应锁定焦点,按Escape关闭并将焦点返回到触发元素。 - 名称:图标按钮具有
aria-label;图片具有alt(装饰性图片设为alt="")。 - 对比度:文本 4.5:1,UI 组件 3:1;颜色不应是唯一的信息传达方式。
- 尊重减少动画;活动区域(
role="status"/role="alert")用于异步结果和错误。 - 测试通过角色和可访问名称进行查询;故事通过可访问性插件;关键页面在 Playwright 中通过 axe 检查。
测试 — 与功能同时编写,而非之后
- Vitest + Testing Library 为每个功能编写单元测试和组件测试,并在同一更改中完成。通过 UI 测试行为(角色、标签、
user-event),而非实现细节。 - 覆盖率 ≥ 80% 行覆盖率且 ≥ 80% 分支覆盖率,由 CI 中的 Vitest 阈值强制执行。
- Playwright E2E 覆盖每个关键用户流程(登录、核心业务流、支付/结账、权限):基于角色的定位器,无固定等待,重用认证状态,关键页面上使用 axe 扫描。
- 每个共享/可复用的 UI 组件都有 Storybook 故事,涵盖所有状态(默认、加载、空、错误、禁用、长内容)。默认使用桌面视口;仅当项目需要响应式布局时才添加移动视口。
- 通过 Storybook + Playwright 截图进行视觉回归;快照测试仅在有针对性的情况下谨慎使用。
安全 — 始终如此
- 不使用
dangerouslySetInnerHTML,除非配合 DOMPurify;在 React 外部使用textContent,绝不使用innerHTML。模型输出和 Markdown 绝不渲染为原始 HTML。 - 不在
localStorage/sessionStorage/IndexedDB 中存储令牌或敏感数据。 认证使用后端设置的HttpOnly会话 cookie;对于 OIDC,优先使用后端为前端(BFF)模式。 - 状态更改请求发送 CSRF 标头;
credentials: 'include'仅在请求自己的 API 时使用。 VITE_*变量是公开的 — 绝不在其中放置任何密钥。?redirect=目标只能是同源相对路径(防止开放重定向)。- 登出会清除服务器会话、TanStack Query 缓存以及所有持久化存储。
- CSP 作为 HTTP 标头,包含
object-src 'none'、base-uri 'none'、frame-ancestors。 postMessage处理程序检查event.origin;绝不合并不可信对象(防止原型污染)。- 前端绝不执行授权 — 由后端负责 [ASVS 8.3.1]。
TypeScript 和 Biome
strict开启(自 TypeScript 7 起为默认值 — 请保持显式设置);同时包括noUncheckedIndexedAccess、verbatimModuleSyntax、moduleResolution: "bundler"、lib使用ES2023+DOM。TypeScript 7 默认types为[]— 请显式列出"vite/client";baseUrl已移除,请使用paths("@/*": ["./src/*"])。- 无
any(使用unknown并进行类型收窄)、无非空断言来抑制错误、无@ts-ignore(如果无法避免,使用带理由的@ts-expect-error)。无魔法数字或字符串 — 使用位于shared/config/或对应切片的config/model中的命名常量。 - 外部数据的类型来自 Zod 模式(
z.infer/z.output),而非可能漂移的手写接口。 - Biome:推荐规则外加
noNestedComponentDefinitions、noReExportAll和noConsole(不在推荐集中);noBarrelFile对于切片的index.ts文件保持关闭(references/structure.md)。 - 确保
biome ci、tsc --noEmit、steiger ./src和knip在本地和 CI 中通过。不要内联禁用规则,除非附带注释解释原因。 - 注释使用英文;TODO 使用项目的 TODO 标记。
仓库卫生 — 始终如此
- Node 版本锁定:
.nvmrc(LTS)、package.json中的engines配合engine-strict、packageManager指定确切的 npm/pnpm 版本;锁定文件提交。 - pre-commit 为提交和推送安装:对暂存更改和所有未推送的提交运行 gitleaks、Biome;推送时运行
tsc、相关的 Vitest 测试和依赖审计。切勿使用--no-verify绕过。 - CI 中每次 MR 和定时任务都进行依赖审计。
详情:references/tooling.md。
Docker 和部署
容器镜像、.dockerignore、Compose 和 Kubernetes 由 docker-kubernetes 技能覆盖;前端工作流和检查清单在 references/docker.md 中。每个镜像都有一个 .dockerignore。以下是与前端相关的注意事项:
VITE_*值在构建时被打入包中。一个镜像用于多个环境需要运行时配置:提供一个config.json(或config.js),该文件在容器启动时由环境变量写入,并在应用启动时获取/验证 — 仍然是公开的,仍然没有密钥。- Vite SPA 是静态文件:将其从 Web 服务器上提供,并为客户端路由配置对
index.html的回退、对哈希资源长时间缓存、对index.html设置no-cache,以及来自references/security.md的安全标头。 - React Router 框架模式(SSR)需要运行时Node 服务器(
react-router-serve或自定义服务器)— 它不是静态部署。
编写之后
如需对结果进行安全审查,建议使用 appsec-review 技能。
来源
综合自 Vercel Engineering 的 React 最佳实践(github.com/vercel-labs/agent-skills, react-best-practices,MIT 许可)— 已从 Next.js 适配为 Vite/React Router 加 TanStack Query;规则 ID 保留以供追溯 — 以及 OWASP Secure Agent Playbook(CC BY 4.0;OWASP ASVS 5.0)。结构遵循 Feature-Sliced Design(feature-sliced.design, fsd.how);references/fsd/ 改编自官方 FSD agent 技能(github.com/feature-sliced/skills,MIT 许可)。可访问性目标为 WCAG 2.2。