前端开发工具【免费下载链接】relayRelay is a JavaScript framework for building>项目地址https://gitcode.com/gh_mirrors/relay29/relay点击查看免费下载Relay 对 GraphQL 操作Mutation、Query、Subscription与 Fragment 有严格的命名约束操作名必须以定义它的模块名开头、以 GraphQL 操作类型结尾并且必须在整个项目中全局唯一。本文基于 Relay 官方教程《Organizing Mutations, Queries, and Subscriptions》展开结合仓库中 Relay 编译器Rust 实现的源码讲解这套命名规则背后的强制校验逻辑并给出 Mutation、Subscription、Query 在真实项目中的推荐组织方式——读完即可在自己的 Relay 应用中写出命名合规、易于定位与维护的 GraphQL 操作。命名规则模块名开头 操作类型结尾 全局唯一在 Relay 项目中每个 GraphQL 操作都必须同时满足三条硬性要求操作名必须以定义它的模块名开头module name prefix操作名必须以对应的 GraphQL 操作类型结尾即Query、Mutation或Subscription操作名必须在全局范围内唯一不能与项目中其他任何操作或 Fragment 重名。例如在文件MyComponent.js中定义的 Mutation必须按照MyComponent[MyDescriptiveNameHere]Mutation的格式命名如MyComponentAddCommentMutation在文件MyComponent.react.js中定义的 Query必须按照MyComponent*Query的格式命名如MyComponentUserQuery。NewsFeed组件中如果定义了某些逻辑上并不属于 NewsFeed 的 mutation 或 queryRelay 依然要求其以NewsFeed开头——只要它们定义在该文件中就必须遵守该文件的模块名前缀。这套命名规则并非随意设计。Relay 官方文档教程原文指出该方案源自 Meta 内部的 Haste 静态资源依赖管理系统Haste 强制所有模块名全局唯一从而可以推导出全局唯一的 Relay 名称同时将模块名与 Relay 操作名耦合也让开发者在已知某个名称时能够更快定位对应的 fragment/query/mutation 所在文件。这套规则在 Meta 内部自洽合理但在 OSS开源环境下其收益可能不如内部明显——不过校验逻辑在编译器层面依然强制生效。编译器如何强制校验命名源码级解读这套命名规则不是文档层面的建议而是 Relay 编译器在构建过程中强制执行的项目级校验。在仓库的 Rust 编译器实现中对应校验逻辑位于 validate_module_names.rs并通过 relay-compiler 的 validate.rs 在构建管线中调用validate_module_names(program)。以操作operation校验为例源码逻辑为从操作名称所在源码位置提取模块名extract_module_name即从文件路径推导模块名根据操作类型Query / Mutation / Subscription确定期望的后缀检查操作名是否以模块名开头operation_name.starts_with(module_name)且以Query、Mutation或Subscription中的任意一个结尾validate_module_names.rs#L32-L60任一条件不满足即产生一条InvalidOperationName诊断错误。对 Fragment 同样执行前缀校验Fragment 名必须以模块名开头否则产生InvalidFragmentName错误validate_module_names.rs#L64-L80。实际编译失败时的报错文案源码中定义为{pluralized_string} in graphql tags must start with the module name ({module_name}) and end with {operation_type_suffix}. Got {operation_name} instead.例如若在MyComponent.js中定义了名为AddComment的 Mutation编译器会提示Mutations in graphql tags must start with the module name (MyComponent) and end with Mutation. Got AddComment instead.值得注意的是源码中有一处被注释的校验行TODO: T71484519!operation_name.ends_with(operation_type_suffix)当前未启用。这意味着当前编译器实际强制的是以模块名开头 以任意操作类型词结尾而非严格校验以与自身类型一致的操作类型结尾。在命名时仍应遵守文档规范Mutation 必须以Mutation结尾、Query 必须以Query结尾以保持跨版本兼容性并为将来重新启用严格校验留有余地。全局唯一性约束除命名格式外操作与 Fragment 名称还必须在全局范围内唯一。Relay 编译器在构建项目时会执行相关校验见 relay-compiler 构建流程 中的全局校验环节。这也是命名规则要求模块名 描述性名称 操作类型的原因借助模块名的唯一性来推导出全局唯一的操作名避免不同文件出现同名操作导致冲突。推荐结构一Mutation 与 Subscription 放入独立 Hook 模块文档给出的核心建议是将 Mutation 放入其自己的 Hook 模块中让操作名更贴近这个操作做了什么what the mutation does而不是哪个组件调用了它which component invokes it。理由很直接命名规则要求 Mutation 名以文件模块名开头如果把 Mutation 定义在组件文件中它的名字就必须以组件名开头例如NewsFeedAddCommentMutation——这会让操作名被调用方而非行为所主导。而将其放入独立的 Hook 模块后名称可以完全描述操作本身。例如要为Post添加给帖子评论的 Mutation可以新建文件useAddPostComment.js并在其中声明名为useAddPostCommentMutation的 Mutation——这是一个描述性极佳的名称见教程文档。如果模块名本身已经足够描述性文档也允许直接在组件所在文件中声明 Mutation但当组件承载多个操作、或操作语义与组件名不一致时独立 Hook 模块是更优选择。同时可以考虑把所有这类 Hook 统一放入专门的hooks目录中集中管理。实际操作示例// hooks/useAddPostComment.js import { useMutation, graphql } from react-relay; // 模块名为 useAddPostComment因此 Mutation 名必须以它开头 const mutation graphql mutation useAddPostCommentMutation($input: AddPostCommentInput!) { addPostComment(input: $input) { postComment { id body } } } ; export default function useAddPostComment() { const [commit, isInFlight] useMutation(mutation); return [commit, isInFlight]; }然后在任意组件中复用该 Hook// Component.js import useAddPostComment from ./hooks/useAddPostComment; function CommentForm({postId}) { const [commitAddComment] useAddPostComment(); const handleSubmit (body) { commitAddComment({ variables: {input: {postId, body}}, }); }; // ... }这样的组织方式带来的收益是双向的操作名useAddPostCommentMutation直接表达了行为语义而定位代码时看到该名称即可推断它定义在hooks/useAddPostComment.js中。Subscription 与 Mutation 的组织方式一致Subscription 通常也是以行为为核心如订阅某条流的更新同样建议放入独立 Hook 模块使 Subscription 名能描述订阅内容本身例如useLiveCommentSubscription。推荐结构二Query 与 Fragment 与组件共存co-location与 Mutation/Subscription 不同文档建议Query 保持与组件紧密耦合根组件Root components应该只有一个 Query该 Query 描述的就是这个组件的数据依赖因此 Query 应与其所服务的组件放在一起Query 与 Fragment 应与使用这些数据的代码共存co-locate with their>// MyComponent.react.js import { graphql, usePreloadedQuery } from react-relay; // Query 与根组件共存名以模块名 MyComponent 开头以 Query 结尾 const MyComponentQuery graphql query MyComponentQuery($id: ID!) { node(id: $id) { ...MyComponent_user } } ; function MyComponent({queryRef}) { const data usePreloadedQuery(MyComponentQuery, queryRef); // ... }而子组件通过 Fragment 声明自身数据依赖// UserAvatar.js import { graphql, useFragment } from react-relay; // Fragment 名以模块名 UserAvatar 开头 const UserAvatar_user graphql fragment UserAvatar_user on User { name avatarUrl } ; function UserAvatar({user}) { const data useFragment(UserAvatar_user, user); return img src{data.avatarUrl} alt{data.name} /; }这样任何 Fragment 或 Query 的名称都可以直接映射到其定义文件形成名称即路径的可定位性。命名组织策略小结操作类型推荐存放位置推荐命名格式示例Mutation独立 Hook 模块可集中放入hooks/目录模块名 描述 MutationuseAddPostCommentMutationSubscription独立 Hook 模块模块名 描述 SubscriptionuseLiveCommentSubscriptionQuery根组件文件与组件共存模块名 描述 QueryMyComponentUserQueryFragment使用该数据的组件文件模块名 描述_字段名前缀惯例UserAvatar_user核心原则可归纳为三句话Mutation/Subscription 按行为命名并独立成模块让名称描述操作本身而非调用方Query/Fragment 按数据使用位置命名并共置让数据依赖声明紧贴消费它的代码所有名称遵守模块名前缀 操作类型后缀 全局唯一这是编译器ValidateModuleNames强制执行的硬性约束也是 Relay 项目可维护性的基础。适用前提与注意事项上述命名规则的强制校验基于文件模块名。Relay 编译器从文件路径提取模块名源码见 extract_module_name.rs因此文件命名应稳定、描述性强改名文件将直接影响其中所有操作的合法名称。该规则源于 Meta 内部的 Haste 依赖管理系统在 OSS 项目中使用时虽然 Haste 并不存在但编译器仍会强制执行模块名前缀校验可通过 Relay 配置中的enforceModuleNamePrefixForNonHaste相关选项控制见 validate.rs因此 OSS 项目中同样需要遵守。命名以模块名开头 以操作类型结尾是文档规范当前编译器版本对后缀必须与操作自身类型一致的严格校验尚处于 TODO 状态建议按规范完整命名以保证未来兼容性。通过遵循这套命名与组织规范你的 Relay 项目将获得全局唯一、可定位、语义清晰的操作名称体系既满足编译器的强制校验也让大型应用的维护成本大幅下降。如需查看本主题的官方原文可参阅教程文档v17.0.0 版本与当前版本教程。赞分享前端开发工具【免费下载链接】relayRelay is a JavaScript framework for building>项目地址https://gitcode.com/gh_mirrors/relay29/relay点击查看免费下载相关推荐在 Relay 中组织 Mutation、Query 与 Subscription命名规则与工程实践在 Relay 中组织 Mutation、Query 与 Subscription命名规则与工程实践 Relay 对 GraphQL OperationMu前端开发工具WinApps旧电脑部署Windows应用4GB内存2核起步的完整方案WinApps旧电脑部署Windows应用4GB内存2核起步的完整方案 本文用WinApps在4GB内存、双核的旧Linux电脑上装Windows 11虚拟机桌面应用虚拟化InversifyJS性能剖析9组基准测试数据揭秘绑定注册与依赖解析的真实耗时InversifyJS性能剖析9组基准测试数据揭秘绑定注册与依赖解析的真实耗时 InversifyJS 是一个为 TypeScript 与 JavaScrip后端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?