Sentry 前端开发规范与实践指南:基于 static/AGENTS.md 的 React 19 + TypeScript 开发手册
【免费下载链接】sentryDeveloper-first error tracking and performance monitoring项目地址: https://gitcode.com/GitHub_Trending/sen/sentry
Sentry 的
static/目录承载着整个 Web 前端的核心代码。这篇指南以仓库根目录下 static/AGENTS.md 为骨架,系统梳理 Sentry 前端的技术栈选型、工程目录结构、代码风格红线、数据请求范式、React 渲染纯度约束、设计系统用法、测试方法论与 SDK 埋点规范,并结合package.json、rspack.config.ts、src/sentry/features/temporary.py等源码级证据深入讲解其落地细节。读完这篇指南,你将掌握在 Sentry 这样的大型监控产品前端中组织页面、发起 API 请求、使用设计系统原语、编写测试以及埋点打标签的完整实战方案,也能直接迁移其中的工程实践到自己的 React 项目中。
一、为什么需要这份前端开发指南
Sentry 是一个典型的前后端分离的大型单体仓库:后端是 Django(Python),前端是位于static/目录下的单页应用。正如 static/AGENTS.md 开头所述,这份文档是AI Agent 和前端工程师在static/目录下开发时的唯一事实来源(source of truth),根目录的 AGENTS.md 则提供了跨前后端的通用命令指引。
仓库按职责划分了多个 AGENTS.md,覆盖不同开发领域:
| 工作范围 | 读取的 AGENTS.md |
|---|---|
前端(static/**/*.{ts,tsx,js,jsx,css,scss}) | static/AGENTS.md |
后端(src/**/*.py) | src/AGENTS.md |
测试(tests/**/*.py、src/**/tests/**/*.py) | tests/AGENTS.md |
| 全局概览与通用命令 | AGENTS.md |
二、前端技术栈全景
static/AGENTS.md 明确列出了 Sentry 前端的核心技术栈,结合 package.json 中的依赖版本可以进一步确认:
- 语言:TypeScript。规则第 4 条强制“ALWAYS use TypeScript”,项目根目录的 tsconfig.json 定义了严格类型检查配置。
- 框架:React 19(
package.json中react: 19.2.3、react-dom: 19.2.3)。 - 构建工具:Rspack(Webpack 的替代品),配置在 rspack.config.ts。
package.json中的build脚本即rspack --config ./rspack.config.ts。 - 包管理:pnpm(
packageManager: pnpm@10.30.2),使用pnpm-lock.yaml锁定依赖。 - 状态管理:Reflux + React Query(TanStack Query)。注意这是 Sentry 的过渡期技术状态——Reflux 是历史遗留,新代码明确禁止新增 Reflux store(见下文"代码风格红线")。
- 样式方案:Emotion(CSS-in-JS)+ Less。CSS-in-JS 用于组件级样式,Less 文件位于 static/less 目录。
- 测试:Jest + React Testing Library(RTL),见 jest.config.ts。
值得注意的几个"非主流"选型:Oxlint 取代 ESLint(oxlint.config.ts+@oxlint系列依赖),Rspack 取代 Webpack(@rspack/core@2.2.0),pnpm 取代 npm/yarn。这些都是在工程化上追求性能与规模化的结果。
三、重要文件与目录地图
static/AGENTS.md 给出了前端代码的完整目录约定,这是理解 Sentry 前端组织方式的关键:
| 职责 | 路径约定 |
|---|---|
| 组件 | static/app/components/{component}/ |
| 页面视图 | static/app/views/{area}/{page}.tsx |
| 状态存储 | static/app/stores/{store}Store.tsx |
| Action 创建器 | static/app/actionCreators/{resource}.tsx |
| 工具函数 | static/app/utils/{utility}.tsx |
| 类型定义 | static/app/types/{area}.tsx |
| API 客户端 | static/app/api.tsx |
从仓库结构看(见 static/app 目录),这套约定被严格执行:components下按功能分子目录(如components/core、components/badge、components/events),views下按业务域组织页面。
3.1 路由(Routing)
- 路由统一在 static/app/routes.tsx 定义;
- 遵循 React Router v6 模式(
package.json中react-router-dom: 6.30.3); - 路由组件尽可能懒加载:
React.lazy(() => import('...'))。这与 rspack.config.ts 中SHOULD_LAZY_COMPILATION(懒编译入口之外的路由)的构建优化策略一脉相承——懒加载同时作用于运行时(React.lazy按需拆包)与编译期(Rspack 懒编译节省内存和启动时间)。
四、前端 API 请求:从 useApiQuery 到 apiOptions 的范式迁移
static/AGENTS.md 的 "Frontend API Calls" 一节规定了一个明确的硬性迁移方向:
使用
useQuery+apiOptions发起 API 请求;永远不要使用已废弃的useApiQuery/getApiQueryData/setApiQueryData;staleTime是必填参数。
虽然useApiQuery在仓库中仍有使用(例如 static/app/components/charts/useSessionsRequest.tsx、static/app/components/events/autofix/useAutofixSetup.tsx),但这些都属于存量代码,新代码一律走apiOptions范式。详细的完整指南(条件请求、调用点泛型、响应头/分页)由frontend-data-fetching技能(.agents/skills/frontend-data-fetching/SKILL.md)承载。
4.1 基本用法
import {skipToken, useQuery} from '@tanstack/react-query'; import {apiOptions} from 'sentry/utils/api/apiOptions'; // 基本用法 const query = useQuery( apiOptions.as<ResponseType>()('/organizations/$organizationIdOrSlug/endpoint/', { path: {organizationIdOrSlug: organization.slug}, staleTime: 30_000, }) ); // 条件请求 —— 将 skipToken 作为 path 传入即可禁用该查询 const query = useQuery( apiOptions.as<ResponseType>()('/organizations/$organizationIdOrSlug/items/$itemId/', { path: itemId ? {organizationIdOrSlug: organization.slug, itemId} : skipToken, staleTime: 30_000, }) );4.2 关键规则
staleTime必填:必须显式选择一个值——0、毫秒数、Infinity或'static'。- 围绕
apiOptions做抽象,而不是围绕useQuery:返回 options 对象,让调用方自行决定传给useQuery、useQueries还是prefetchQuery。 - 缓存存的是
{json, headers}而非裸 body:apiOptions默认用select提取.json,但getQueryData、setQueryData、retry函数与predicate回调收到的都是完整的ApiResponse<T>结构。 - 不要在 Query 中使用
api.requestPromise:它会返回错误的结构;如需手写queryFn,改用apiFetch。
4.3 类型推断:禁止调用点泛型
技能文档强调了一个极易踩坑的点——永远不要在useQuery、useMutation、queryOptions、mutationOptions的调用点传类型参数,让 TypeScript 从queryFn/mutationFn推断:
// ❌ 错误:给 useMutation/useQuery 传泛型 useMutation<ResponseType, RequestError, Variables, Context>({...}) // ✅ 正确:标注 mutationFn,让类型从函数签名推断 useMutation({ mutationFn: (variables: MyVariables) => fetchMutation<MyResponse>({...}), })具体规则:类型化mutationFn的参数而不是 hook 泛型;用fetchMutation<T>标注返回值;错误类型不要显式写成RequestError(这本质上是类型断言),用if (error instanceof RequestError)做运行时收窄;不要显式声明 context 类型,让它从onMutate的返回值推断。
五、General Frontend Rules:五条红线
static/AGENTS.md 定义了五条不可逾越的前端规则,它们共同决定了 Sentry 前端代码的形态:
- 禁止新增 Reflux store:Reflux 属于历史遗留,状态管理的新代码一律使用 TanStack Query(服务端状态)+ React 内置状态/其他方案(客户端状态)。
- 禁止类组件:全部使用函数组件 + Hooks,这也与 React 19 的时代背景一致。
- 禁止 CSS 文件:优先使用 core 组件(
[static/app/components/core](https://link.gitcode.com/i/de705a0aecd4ddedc06911ea0859218e)),仅在真正边缘的情况下使用 Emotion。 - 必须使用 TypeScript:无类型代码不允许进入代码库。
- 测试必须与源码同目录(colocate):
*.spec.tsx紧邻被测组件,例如 static/app/components/core/datetime.spec.tsx 紧挨着datetime.tsx。 - 路由懒加载:
React.lazy(() => import('...'))。
这些规则的价值在于可预见性:任何工程师或 Agent 打开一个不熟悉的目录,都能根据目录约定和代码形态快速定位组件、视图、store 与测试。
六、Refs 使用纪律:渲染必须纯净
static/AGENTS.md 的 "Refs" 一节给出了一个非常具体的 React 陷阱——渲染期间读写ref.current会破坏 React 的渲染纯净性:render 必须是纯函数,而 ref 是 React 不追踪的可变状态。渲染期读 ref 可能在并发渲染下拿到过期值,写 ref 则制造了副作用、让 render 变得不纯。
正确姿势:只在 effect(useEffect/useLayoutEffect)或事件处理器/回调里读写ref.current;渲染期需要值就从 props/state 派生const,或提升到useState/useMemo;只有"必须跨渲染持久且不触发重渲染"的值(DOM 节点、定时器、供 effect 后续读取的 previous-value 跟踪)才值得用 ref。
// ❌ 渲染期间读写 ref —— 副作用 + 并发渲染下的过期值 function Component({value}: Props) { renderCountRef.current += 1; // 渲染期副作用 const previous = prevValueRef.current; // 并发渲染下可能过期 prevValueRef.current = value; // 渲染期写入 return <div>{previous}</div>; } // ✅ 在 effect 中改 ref,渲染值靠派生 function Component({value}: Props) { const prevValueRef = useRef(value); useEffect(() => { prevValueRef.current = value; // 在 effect 中写入 }, [value]); return <div>{value}</div>; }七、UI Patterns 与 Design System
7.1 剪贴板复制模式
当需要实现"高级复制到剪贴板"功能(如复制 Markdown、JSON 等不同格式)时,不要为每种格式各做一个按钮,而应使用sentry/components/copyAsDropdown(源码位于 static/app/components/copyAsDropdown.tsx),在一个下拉菜单中提供不同格式选项。这统一了交互模式,避免每个页面自造轮子。
7.2 设计系统:从 @sentry/scraps 取原语
Sentry 的设计系统核心是@sentry/scraps包——优先使用它的核心原语,而不是手写 styled components 来做布局和排版。完整参数/token 参考见design-system技能(.agents/skills/design-system/SKILL.md)。关键约束如下:
- 布局:用
Flex、Grid、Stack、Container,禁止手写display: flex/grid。 - 排版:用
Text和Heading,禁止裸用<p>、<span>、<div>或<h1>–<h6>。 - 优先组件 props 而非
style属性:用gap/padding 而不是margin。 - 用响应式 props 替代媒体查询:例如
{xs: 'column', md: 'row'}表示在移动端纵排、中等宽度以上横排。 - 布局与排版分离:
Flex/Grid负责布局,Text/Heading负责排版,不要耦合在同一个 styled component 里。 - 优先
InfoTip/InfoText而非裸Tooltip。 - 新组件要附带
*.stories.mdxstory。 - 优先 core 组件(static/app/components/core 下已有
alert、button、avatar、badge、checkbox、disclosure、form、input、modal、tabs、table、text、tooltip等数十个原语),Emotion 只留给真正的边缘场景。
7.3 其他核心组件约定
- 头像:用
static/app/components/core/avatar下的<UserAvatar/>/<TeamAvatar/>/<ProjectAvatar/>等(列表用<AvatarList>),禁止裸<img>。 - 折叠/展开:用核心
<Disclosure>组件,不要自己手写 expand/collapse。 - 图标:从
sentry/icons导入,图标文件放在static/app/icons(入口见 static/app/icons/index.tsx),禁止内联 SVG;用 svgo/svgomg 优化。 - 图片:通过
sentry-images别名(webpack loader)导入,图片放在static/app/images,禁止用静态路径引用。
八、React 测试:RTL 与 MockApiClient
前端测试(*.spec.tsx、React Testing Library、MockApiClient、路由/网络测试)的完整方法论由react-testing技能承载(.agents/skills/react-testing/SKILL.md),涵盖查询优先级、禁止 mock hook、fixtures 用法、异步断言、网络请求 mock 等主题。
工程层面的测试入口在 package.json:pnpm test-ci <file_path>运行指定文件(例如pnpm test-ci components/avatar.spec.tsx),pnpm test为带--watch的开发模式。测试文件与源码同目录存放,这是上面"五条红线"之一。
九、Sentry SDK 埋点:遵循 OTel/Sentry 约定命名
在Sentry.setTag/setContext或 span 的setAttribute之前,先检查@sentry/conventions是否已有标准命名。复用约定名能保证属性可查询、且与其他生产者(SDK、Relay)对同一概念发出的数据一致——自定义名字会把同一份数据割裂到两个 key 上。这条规则与后端技能backend-conventions中的 Python 侧规则保持同步,因为前端 span 和后端 span 可能描述同一次请求。
注意:单个名称常量(如USER_AGENT_ORIGINAL)位于/attributes子路径下,而不是包根路径;包根只重新导出元数据表——ATTRIBUTE_METADATA(每个属性的完整记录:简介、类型、别名、废弃状态)和ATTRIBUTE_SEARCH_METADATA(搜索字段 UI 渲染的描述,见 static/app/utils/fields)。
import {USER_AGENT_ORIGINAL} from '@sentry/conventions/attributes'; // 错误:给一个 conventions 已覆盖的概念发明新名字 span.setAttribute('request_user_agent', navigator.userAgent); // 正确:使用既有约定名 span.setAttribute(USER_AGENT_ORIGINAL, navigator.userAgent);新增候选命名前,先 grepnode_modules/@sentry/conventions/dist/attributes.d.ts确认是否存在。这些约定由 OTel 语义约定加上 Sentry 自身的模型生成。
十、前后端联动的两条协作规则
10.1 Feature Flags(FlagPole)
Sentry 用 FlagPole 管理功能开关(src/sentry/features 目录,配置见 src/sentry/features/temporary.py)。新功能必须藏在 flag 后面:在temporary.py中注册,Python 侧用features.has(...)检查,前端用organization.features.includes(...)检查。完整的注册/api_expose/测试/灰度流程见feature-flags技能(.agents/skills/feature-flags/SKILL.md)。
从temporary.py源码可以看到 flag 注册的三种作用域与策略组合:
| 作用域 | 策略 | 示例 |
|---|---|---|
| SystemFeature | INTERNAL | organizations:create(默认开启) |
| OrganizationFeature | FLAGPOLE | organizations:ai-issue-detection、organizations:authv2-rollout |
| ProjectFeature | FLAGPOLE | 项目级灰度功能 |
值得注意的细节:temporary.py的 docstring 明确警告这些 flag只用于门控新开发功能,且注定要被移除("CLEAN UP YOUR FEATURE FLAGS!"),因为遗留 flag 会引入长期理解的复杂度。删除已完成的 flag 或 option 有固定的 PR 顺序,需使用remove-option-or-flag技能。
10.2 前后端分离部署
前端(static/)与后端(src/、tests/)不是原子化部署的,CI 会强制检查:
- 同时改动前后端时必须拆成两个独立 PR;
- 前端依赖新的 API 时,先合入后端 PR;
- 纯测试新增与
src/改动放在同一个 PR 是允许的。
十一、常用开发命令速查
虽然 static/AGENTS.md 主要聚焦代码规范,其引用的根目录 AGENTS.md 给出了前端日常命令,整理如下:
# 开发环境 pnpm run dev # 完整 dev server(需要先 devservices up) pnpm run dev-ui # 仅前端 + 热重载,API 代理到生产 sentry.io # 类型检查(检查整个项目,不接受文件路径参数;不要直接用 tsc) pnpm run typecheck # Lint / 自动修复 pnpm run lint:js # 全部 JS/TS pnpm run lint:js components/avatar.tsx # 指定文件 pnpm run fix # 自动修复 # 测试 pnpm test-ci <file_path> # 运行测试 pnpm test-ci components/avatar.spec.tsx # 指定文件对应的环境准备:SENTRY_DEVENV_FRONTEND_ONLY=1 devenv sync(跳过迁移,推荐用于纯前端/pytest 场景)、direnv allow、devservices up。前端构建配置 rspack.config.ts 中还支持SENTRY_UI_HOT_RELOAD、ENABLE_TS_CHECKER(类型检查资源开销大,需显式开启)、LAZY_COMPILATION(懒编译,节省内存和启动时间)、USE_TANSTACK_DEVTOOL等环境开关。
十二、总结:一份可迁移的前端工程实践
回顾 static/AGENTS.md 的全部内容,它本质上是 Sentry 前端团队多年工程经验的浓缩,其可迁移价值在于:
- 显式技术栈 + 目录约定:让千人大仓保持可导航性;
- "禁止"清单文化:禁 Reflux、禁类组件、禁 CSS 文件、禁渲染期读写 ref、禁调用点泛型——每条禁令背后都是一个真实的线上教训;
- 把复杂决策委托给技能(skills):数据请求、测试、设计系统、特性开关等主题都有独立技能文档承载完整方法论,AGENTS.md 保持精简、可扫描;
- 前后端规范对齐:埋点命名约定在前后端保持同步,保证同一请求的两端数据可关联查询。
对于正在构建大型 React 应用的团队,这份文档提供了一个经过生产验证的参照系:如何用文档约束 Agent 与工程师、如何用目录和红线维持代码一致性、如何用约定名保持遥测数据的可查询性。
【免费下载链接】sentryDeveloper-first error tracking and performance monitoring项目地址: https://gitcode.com/GitHub_Trending/sen/sentry
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考