从 2016 年产品需求与核心价值出发,理解 Relay 的设计哲学与架构演进
前端开发工具【免费下载链接】relayRelay is a JavaScript framework for building>项目地址https://gitcode.com/gh_mirrors/relay29/relay点击查看免费下载本文以 Relay 核心团队 2016 年 2 月的产品需求与核心价值观会议记录meta/meeting-notes/2016-02-05-product-requirements-and-core-values.md为骨架逐条梳理当年被列为痛点、目标与非目标的产品问题并结合当前仓库relay-runtime、react-relay、Rust 编译器 compiler/crates的源码实现说明这些早期判断如何在今天演化为具体的 API 与架构决策。读完本文你将理解 Relay 的 fetch policy、store 垃圾回收、mutation 配置体系、变量传递规则等核心机制背后为什么这样设计。一、文档背景一次核心团队的产品脑暴这份会议记录记录了 Relay 核心团队的一次头脑风暴目标是盘点使用 Relay 的产品开发者遇到的痛点与未解决的功能请求同时讨论 Relay 的高层价值主张。它分为三大部分Product Requirements产品需求覆盖性能、一致性/陈旧性控制、Mutations、可扩展性、变量理解、非 Facebook GraphQL、开发者体验、新功能愿望清单Core Values核心价值团队认为需要长期维持的六大设计原则Non-Goals非目标明确不做什么的设计边界。这份文档并非教程而是理解 Relay 十年架构走向的一把钥匙今天 relay-runtime 中的大量 API几乎都能在这份清单里找到最初的动机。二、性能Fast by Default 的工程化实现文档将性能列为第一条痛点具体拆为三点快速启动与达到可交互时间TTI管理资源消耗包括缓存淘汰 cache eviction / 垃圾回收 GC维持应用响应性。这三点在当前 RelayModernStore.js 中有完整对应的实现机制引用计数与 retain 机制store 通过retain(operation)返回一个可释放disposable的引用_gcHoldCounter维护被持有引用的数量只有引用计数归零的数据根节点才会进入可回收状态见 RelayModernStore.js释放缓冲release buffer与垃圾回收调度store 维护一个容量受gcReleaseBufferSize默认值见DEFAULT_RELEASE_BUFFER_SIZE约束的释放缓冲超出上界时淘汰最旧的条目并调用scheduleGC()见 RelayModernStore.js可插拔的 GC 调度器gcScheduler选项默认使用resolveImmediate允许环境注入自定义调度策略这正是文档中不同 scheduling policiessync/async需求的雏形。从源码结构可以推断Relay 从一开始就把只保留正在被使用visible的数据作为资源管理的基本立场而不是把整个已拉取历史长期驻留内存。文档中 Mutations 一节那句performance proportional to what is visible, not entire history of what has been fetched性能应与可见内容成正比而不是与已拉取历史成正比正是这套引用计数 GC 设计想要达成的目标。三、一致性与陈旧性从何时强制刷新到 Fetch Policy文档对一致性问题的描述非常精准何时强制拉取force fetch控制陈旧性 → 导致强制拉取 → 强制拉取可能导致记录缺字段、列表缺记录。这条因果链在今天被完整映射到Fetch Policy 体系上。在 RelayRuntimeTypes.js 中定义了export type FetchQueryFetchPolicy store-or-network | network-only; export type FetchPolicy FetchQueryFetchPolicy | store-and-network | store-only;useLazyLoadQuery.js 对四种策略给出了精确语义FetchPolicy是否复用本地缓存是否发网络请求适用场景store-or-network默认是仅在缓存缺数据时常规查询缺什么补什么store-and-network是总是发送需要立刻渲染缓存又保证刷新network-only否总是发送强制刷新忽略本地缓存store-only是永不发送纯本地数据操作 / 由调用方负责拉取强制拉取可能导致缺字段这一担忧同样有实现层面的回应store 在判断数据是否可用时依赖 DataChecker.js 的Availability检查available | missing一旦发现某字段缺失就会从网络层补齐但若开发者使用network-only绕过 store 检查则渲染的是刚从网络返回、尚未归一化补齐的数据视图。此外fetchKey选项可强制对同一查询在变量未变化时重新评估见 useLazyLoadQuery.jsCacheConfig中的force: true用于绕过网络层的响应缓存poll支持按毫秒间隔轮询更新见 RelayRuntimeTypes.js。可以说一致性/陈旧性控制从 2016 年的待解问题演变成了今天fetchPolicy/fetchKey/CacheConfig这套可组合的控制面。四、Mutations从配置混乱到声明式与命令式并存文档把 Mutations 列为痛点集中的区域与应用中非 Relay 部分的一致性range 等配置令人困惑、缺乏反馈时容易出错性能应与可见内容成正比当乐观更新相对 fat query 缺数据时应能感知并警告开发者。当前仓库给出了双层解决方案声明式配置DeclarativeMutationConfigcommitMutation的configs参数支持RANGE_ADD、RANGE_DELETE等声明式更新见 commitMutation.js底层由 ConnectionHandler.js如buildConnectionEdge见 ConnectionHandler.js与 MutationHandlers.js 实现连接的边插入/删除。文档抱怨range/other configs 令人困惑——从源码看Relay 的回应是把连接更新集中到 connection handler 中做标准化处理减少每个开发者各自手写易错的记录操作命令式 updateroptimisticResponse/optimisticUpdater/updater提供完全命令式的 store 修改入口。特别地optimisticResponse会被传入validateMutation做校验见 commitMutation.js这正是文档中知道乐观更新相对 fat query 缺数据并警告开发者这一诉求的直接实现——Relay 会比对乐观响应与 mutation 声明缺字段时给出错误反馈。五、可扩展性与调度策略局部隔离 分策略缓存文档在 Scalability 一节提出按应用的各个 section 隔离性能/一致性不同的调度策略同步/异步不同的缓存/陈旧性策略。从当前仓库看这些诉求通过组合式 API 可配置 store得到回应按查询/组件粒度控制缓存策略store-only、store-and-network允许不同组件对同一 store 采用不同缓存态度见 useLazyLoadQuery.js渲染策略可调UNSTABLE_renderPolicy?: RenderPolicyfull | partial允许部分渲染配合 store 的gcScheduler/gcReleaseBufferSize构造参数见 RelayModernStore.js可以在不改造业务代码的前提下调整整体的调度与资源回收行为容器外部观察数据observeFragmentExperimental、observeQueryExperimental、waitForFragmentExperimental见 store 目录让数据订阅不限于容器内部为隔离不同 section 的一致性提供了新入口。六、理解变量route params 与重复子组件的变量传递文档记录了开发者对变量传递的三类困惑如何在容器中访问路由参数prepareVariables未文档化、执行时机不可预测导致持久化查询困难为什么查询和 props 中都要传变量——文档自己给出了答案是为了区分同一父组件中被嵌入两次、但 props 不同的同一子组件。从当前仓库源码可以印证这一设计RelayModernSelector中提供getVariablesFromFragment见 index.js 与 readUpdatableFragment.js其核心作用是把父级变量按 fragment 声明的依赖关系投影project到子 fragment 的变量集合。变量必须同时出现在查询与 props 中正是因为每个 fragment 的变量空间是独立求值的——同一子组件被复用两次时Relay 依赖父查询变量 props 中的 fragment 引用来区分两份不同的数据身份。这与文档中记录的动机完全一致也解释了为什么 Relay 强调数据依赖与视图逻辑 co-location。七、非 Facebook GraphQL连接分页与 schema 无关性文档明确列出对非 Facebook GraphQL 服务的诉求更多分页选项如 windowed pagination 或非 cursor 分页移除 node、viewer 等 Facebook GraphQL 特化概念。当前仓库的回应是Relay 保持GraphQL 协议中立。连接抽象位于 runtime 的 connection handlerhandlers/connection而不是编译器强制的 schema 形状schema 的定义与校验由 relay-schema、schema 等 crate 完成同时支持通过 schema 扩展见 schema-extensions接入自定义指令与字段。可以推断当年去除 node/viewer 依赖的方向最终体现为 Relay 对任意合法 GraphQL schema 的适配能力而 cursor 分页只是 handler 层的默认实现之一。八、开发者体验从 Fragments 派生 Flow 类型文档在 Developer experience 一节提出两项诉求从 fragments 派生 Flow 类型解决到处判空的方案。这两项如今由编译器体系完成Rust 编译器中的 relay-typegen crate 负责从 GraphQL fragment/query 生成精确的 Flow 与 TypeScript 类型其测试目录 relay-typegen/tests 下有数百个输入输出对从而让data对象的类型严格匹配查询形状。文档到处判空的痛点在类型层面体现为可空字段的显式类型例如data.user?.name并在运行时配合>赞分享前端开发工具【免费下载链接】relayRelay is a JavaScript framework for building>项目地址https://gitcode.com/gh_mirrors/relay29/relay点击查看免费下载相关推荐bRPC核心架构与设计哲学bRPC核心架构与设计哲学 bRPC作为工业级RPC框架采用清晰的三层架构设计Server、Channel、Controller和创新的bthread协程RPC框架后端微服务网络通信终极指南如何在15分钟内用kpatch实现Linux内核零重启安全更新终极指南如何在15分钟内用kpatch实现Linux内核零重启安全更新 kpatch是一个Linux动态内核补丁基础设施它允许您在不重启或重启任何进程的情况操作系统驱动开发Gopeed 首次下载唤醒失败一张自检表加 3 条命令修到底Gopeed 首次下载唤醒失败一张自检表加 3 条命令修到底 在 Windows 上点了个磁力链接Gopeed 要么没弹出要么弹出来了却没有任务或者双击网络CLI后端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →