
Langfuse 前端大功能 Controller 迁移指南从巨型组件到受管理功能架构【免费下载链接】langfuse Open source AI engineering platform: LLM evals, observability, metrics, prompt management, playground, datasets. Integrates with OpenTelemetry, LangChain, OpenAI SDK, LiteLLM, and more. YC W23项目地址: https://gitcode.com/GitHub_Trending/la/langfuse本文是 Langfuse 开源仓库.agents/skills/frontend-large-feature-architecture技能体系中 controller-migration 指南的实战化解读。它面向 traces、observations、experiments、prompts、evals、datasets、sessions 等已经膨胀为数据获取 路由胶水 过滤器 表格/列表状态 选择 抽屉 动作 昂贵渲染一肩挑的巨型 controller 组件给出了一条可执行、可度量、可复用的渐进式迁移路径。读完本文你将掌握如何为一次迁移 PR 划定边界、如何按 14 步把 controller 拆解为页面生命周期 本地 store 命名 actions 纯函数数据准备 view-only 渲染的分层结构以及如何用 feature README 持续记录迁移债务与下一步切片。一、为什么需要 Controller 迁移在 Langfuse 前端web/src/features/*中controller 指代一个同时拥有数据获取、视图状态、表格/列表状态、副作用、动作与昂贵渲染的组件或 hook。问题不在于这个名字而在于单一位置承担了过多变化中的职责一次勾选checkbox、悬停hover或行选择row-selection变化会让构建过滤器、列配置、数据包装器、昂贵单元格、抽屉、动作与路由胶水的代码整体重跑页面组件会为无关的行级变化反复重建列、过滤器、行包装器与动作回调行/单元格越昂贵越需要被窄容器narrow containers隔离成 view-only。因此迁移的目标是清晰的职责归属clear ownership而不是把所有东西塞进一个 store。出发点是 big-feature-rules.md 中定义的 ownership baseline页面/视图拥有生命周期并创建 feature 作用域依赖server/query 状态留在 tRPC / React Query路由状态留在 router / filter hooks高频本地 UI 状态放入 per-mount 的 feature store全局 store 仅用于跨路由/跨 feature 共享的产品状态纯数据准备在渲染前把拉取数据转换为 UI 数据复杂工作流放在actions/*.ts或 store actions 中effect 是集成边界而非普通的状态派生手段昂贵的行/单元格/条目应被窄容器包成 view-only。现状是大多数现存大功能尚未达到理想形态仍有数百个案例待修复big-feature-rules.md 明确提到仅用 effect 派生或同步状态就有数百处。每次改动都必须明确说明这次改进了哪条边界、哪些行为是有意变更的。二、现实主义的迁移策略让下一次变更比上一次更安全不要一开始就设计完美的最终功能。迁移的正确姿势是从让下一次变更比上一次更安全开始而不是一次性推翻重写。对每个迁移 PR写清楚以下六点这正是评审与回滚的决策依据当前被针对的 controller 问题——这一刀切在哪被改进的状态/动作/数据准备/渲染边界——边界从哪移到哪哪些行为有意变更、哪些必须保持不变——例如用 spinner 门控渲染这类 UI 简化往往是对的参见 react-without-useeffect.md 的 golden example在数据加载完成后再渲染 UI用initialValueprop 播种状态在渲染期派生值而非用 effect 同步用哪些 instrumentation 或测试证明这次切片有效该功能中仍然散落的部分——明确记录剩余债务推荐的下一片切片。这正是大功能变成受管理功能managed features的方式feature README 是活的所有权地图living owner map随功能演进就地更新而不是等完美重构后再补文档。三、Step-by-Step 迁移路径14 步以下 14 步是迁移的完整路径每一步都应尽量形成一个独立、可评审、可回滚的切片绘制 controller 地图列出组件拥有的每一组状态、查询、派生值、effect、回调、动作与昂贵的子渲染分类状态区分 server/query 状态、路由状态、持久化浏览器状态、高频本地 UI 状态、派生视图数据、命令式集成状态、一次性 modal/form 状态度量一个症状挑一个具体交互行选择、滚动、过滤器变化、抽屉打开、表单步骤变化、saved-view 变化测量它触发多少 rerender、remount、refetch、recalculate选择一条边界从高价值边界入手——选择selection、懒加载行状态、批量动作工作流、filter-target 映射、列/视图状态、向导步骤状态、纯数据准备。逐边界推进即使一次改动改善多条边界也保持切片原子选择最轻的工具纯 helper 或动作抽取可能是正确的第一步只有当需要**选择性订阅selective subscriptions**或per-mount 持久化时才加本地 store按需创建本地 store 实例在页面/视图中用懒useState创建只通过 context 提供这个稳定实例const [store] useState(() createFeatureStore(initialState));仓库实证这一模式已落地于web/src/features/datasets/components/DatasetsTable.tsxuseState(() createDatasetsTableStore())、web/src/features/experiments/components/table/ExperimentsTable.tsxcreateExperimentsTableStore()以及web/src/features/evals/v2/pages/EvaluatorsPage.tsxcreateEvaluatorsTableStore()。用useState而非useMemo是因为 store 是有状态的基建其身份应被当作状态对待一个提交后挂载的视图对应一个 store 实例卸载时销毁——useMemo只是渲染期派生值的缓存不是外部 store 实例的归属边界详见 local-feature-state.md。把变更移入命名 actions状态变更逻辑放入 store actions 或外部 action 函数组件只负责用户事件不负责工作流拆分容器与视图容器containers可以订阅、调用 hooks、拉取数据视图views只渲染 props 或小而精的选择值把数据准备移出渲染昂贵或复杂转换放入纯函数。后端数据单向流动fetched data - compiled UI data - render显式桥接查询状态当本地 store 决策依赖 React Query 数据时用命名 feature hook 或 action 表达该关系参见 big-feature-rules.md 的 Hard Rule 5隔离命令式集成virtualizer、observer、键盘监听、第三方 DOM 变更处理、timer 都属于窄集成 hookseffects 的合法场景就是具名的外部系统集成 清理更新 feature README记录该 PR 改进了什么、期望边界、已知散落状态、下一个抽取目标移除调试 instrumentation临时日志在迁移期有用但不应随切片存活重复每片切片让一个语义交互变得更窄、更易推理。四、按功能选择首个切片Feature-Specific First Slices不要照搬统一模板依据功能当前形态选择第一刀切在哪里功能面推荐首个切片Traces / Observations 表格行选择、全选select-all、批量动作、昂贵单元格包装器Session 详情与事件隔离虚拟化、懒加载行状态、动态测量与渲染行内容解耦Experiment 结果表格选中行状态、filter-target 映射、run/evaluation 批量动作、比较列构建的纯 helperExperiment 创建向导分离已提交表单数据与展示状态active step、选中 prompt 标签、schema 显示名、evaluator 选择Prompt 管理拆分 prompt 详情路由/查询状态、标签/版本选择、prompt 历史数据准备、mutation 工作流Eval 模板与 Evaluator 表单在加 store 之前先抽取表单默认值、model/provider 准备、校验 helper、提交工作流Datasets 与 Dataset runs隔离 active-cell/compare-field 状态、表格选择、run 比较准备、上传/导入工作流需要特别警惕如果某个功能已经有部分 hooks 承担了相关工作那只能算部分迁移不能证明整个 surface 健康。Langfuse 中 traces 的目录结构web/src/features/traces/下分设components/、contexts/、fns/、hooks/、stores/、parsers/正是这类渐进拆分的产物但仍被技能文档列为待迁移候选——不要因为某个大型组件今天能跑就照抄它big-feature-rules.md Hard Rule 9。五、验收标准Acceptance Criteria一次合格的迁移切片必须满足本地状态变更只唤醒订阅了该状态的组件——勾选一行不触发整页重建页面组件不再为无关的行级变化重建列、过滤器、行包装器与动作回调昂贵行/单元格是窄容器背后的 view-only 组件effect 是外部系统集成边界而不是初始化或普通数据派生手段复杂工作流可以在不渲染页面的情况下被调用——action 是独立函数不依赖组件渲染上下文feature README 告诉下一个开发者什么已改进、什么仍然散落、下一个改进应指向哪里。六、必须避免的反模式Avoid迁移过程中明确禁止用巨型全局 store 替换巨型组件——全局 store 只留给真正跨 feature/跨路由的产品状态不要为了消除 prop drilling 或让组件变小而把本地状态提升为全局参见 local-feature-state.md 的 Anti-Patterns在缺乏度量验收标准时一次移动所有状态把 memoization 当作架构——memo有帮助但修复不了坏的状态边界useCallback/useMemo属于过早优化正确手段是拆组件而不是记忆化把 provider 耦合的 store hooks 放进共享的src/components/*导出——共享组件调用 feature 作用域 store hook 会静默破坏未挂载 feature provider 的其他调用方view 作用域的 Zustand consumers 应放在src/features/*因为页面恰好拥有全部依赖就把工作流留在页面内联——不要为了一个按钮能执行功能级工作而把二十个 props 穿透整棵组件树big-feature-rules.md 中的await applyBulkAction({ store, queryClient, projectId })就是独立 action 的范例用 README 充当胜利宣言、隐藏剩余 controller 状态——必须明确写出剩余债务。七、落地要点feature README 与本地 store 的配套实践Controller 迁移不是孤立的代码重构需要两件配套产物1. Feature READMEowner map。按 feature-readmes.md 的要求每个大型 feature 文件夹应有一份简短README.md约定优先用README.md而非FEATURE.md与现有web/src/features/*约定一致包含Surface、Entry Points、Structure、External Consumers、State Ownership、Performance And Stability Boundaries、Migration State、Development Context。README 不是 changelog而是记录持久边界 已知散落债务 下一两片原子切片例如就地更新式写法Improved in current shapelocal store owns row selectionexport action 已移到actions/exportFeatureData.tsrow view 不再订阅 filter 状态。Still spreadsaved-view 状态仍在页面 controllerfilter option 准备仍内联mutation 工作流仍闭包页面 hooks。Next slice把 filter-option 准备抽成纯 helper把批量动作工作流移入 action 文件从视图组件拆分 route/query 胶水。2. 本地 store 与独立 actions。状态需要选择性订阅或要在行/条目 remount 后存活时才引入本地 vanilla Zustand store用不可变 plain object 表达键控状态如selectedIds: Recordstring, true避免原地修改Set/Map。复杂工作流放进actions/*.ts或命名 store actionsaction 不调用 React hooks需要上下文时传入 store 实例或小型依赖对象如await exportFeatureData({ capture, fetchDetails, projectId, refetchSummary, selectedIds })。八、迁移节奏总结一次成功的 controller 迁移切片 度量一个症状 → 选择一条高价值边界 → 选择最轻工具纯 helper/action → 本地 store→ 拆容器与视图 → 数据准备移出渲染 → 显式桥接查询 → 隔离命令式集成 → 更新 feature README → 移除临时 instrumentation → 进入下一片。这套方法论的核心判断标准始终是每一次状态变化是否只唤醒语义上依赖它的 UI 与动作。对 traces/observations 表格、sessions、experiments、prompts、evals、datasets 等 controller-heavy 表面从行选择、批量动作、filter-target 映射这类高价值边界入手一小片一小片地把受管理的功能做出来——这正是 Langfuse 前端从遗留巨型组件走向可维护架构的现实路径。进一步的完整规则、store 形态与反模式清单可继续阅读 big-feature-rules.md、local-feature-state.md 与 feature-readmes.md。【免费下载链接】langfuse Open source AI engineering platform: LLM evals, observability, metrics, prompt management, playground, datasets. Integrates with OpenTelemetry, LangChain, OpenAI SDK, LiteLLM, and more. YC W23项目地址: https://gitcode.com/GitHub_Trending/la/langfuse创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考