Ferry代码生成器教程:从GraphQL Schema一键生成不可变强类型类
2026/8/24 17:51:45 网站建设 项目流程

Ferry代码生成器教程:从GraphQL Schema一键生成不可变强类型类

【免费下载链接】ferryStream-based strongly typed GraphQL client for Dart项目地址: https://gitcode.com/gh_mirrors/fer/ferry

Ferry 代码生成器是 Dart/Flutter 生态中一款强大的 GraphQL 代码生成工具。只需一份 GraphQL Schema 和若干.graphql操作文件,它就能通过一条命令自动为你生成不可变、强类型的 Dart 数据类,让 GraphQL 客户端告别运行时错误。本文是面向新手的完整指南,带你从零跑通Ferry GraphQL 代码生成全流程,并对比两代生成器的选型与进阶配置。

为什么需要 GraphQL 代码生成器?

手写 GraphQL 客户端时,你通常要面对这些痛点:

  • ⚠️运行时才报错:字段名拼错、类型不匹配,只有请求发出后才发现
  • 🔁手动解析 JSON:反复写map['field'],空值处理全靠小心翼翼
  • 🐌类型安全缺失:IDE 无法补全,重构时无法自动追踪字段变化

Ferry 的解决方案是:基于你的 GraphQL Schema,在编译期生成完整的强类型类。正如 README.md 中所描述的,它是 "Fully Typed" 的核心能力——编译期检查 + IDE 自动补全,连缓存的读写都是强类型的。

快速开始:4步一键生成强类型类

第 1 步:克隆仓库并添加依赖

git clone https://gitcode.com/gh_mirrors/fer/ferry

在你的 Dart/Flutter 项目中,将ferry_generator加入dev_dependencies,并安装build_runner(详见 packages/ferry_generator/ 目录说明)。

第 2 步:下载 GraphQL Schema

把服务的 Schema 以 SDL 格式保存到lib/目录下,例如lib/schema.graphql

npx get-graphql-schema [ENDPOINT_URL] > lib/schema.graphql

第 3 步:编写 .graphql 操作文件

把 Query、Mutation 和 Fragment 保存为.graphql文件,必须放在lib/目录内。官方建议放入graphql/子目录,例如仓库中的 examples/pokemon_explorer2/lib/graphql/:

query AllPokemon($first: Int!) { pokemons(first: $first) { id name maxHP } }

如果操作引用了其他文件里的 Fragment,用注释导入即可:

# import './pokemon_card_fragment.graphql'

第 4 步:配置 build.yaml 并运行构建

在项目根目录创建build.yaml,指向你的 Schema 文件,然后执行:

dart run build_runner build --delete-conflicting-outputs

完成后,每个.graphql文件旁边都会出现__generated__目录,强类型类已全部就绪 ✅

生成物详解:generated目录里有什么?

all_pokemon.graphql为例,生成器会产出以下文件:

文件后缀作用
*.ast.gql.dartGraphQL 文档的 AST 常量定义
*.data.gql.dart响应数据类(强类型模型)
*.var.gql.dart变量类(操作没有变量时不生成)
*.req.gql.dart请求类,继承OperationRequest,内置 FetchPolicy 等执行配置
*.schema.gql.dartSchema 中的枚举、Input 类型、possibleTypes 映射
*.utils.gql.dart可选的 equals / hashCode 辅助(v2 中按需开启)

以 v1 生成器为例,AllPokemon查询会生成三个核心类:GAllPokemonReq(请求)、GAllPokemonVars(变量)、GAllPokemonData(响应数据),并自动生成 Schema 中用到的 input、enum 和自定义 scalar 支持类。

💡小知识点:所有类名前都会加G前缀,这是built_value包的命名限制,也是识别 Ferry 生成类的标志。

两代生成器怎么选?ferry_generator vs ferry_generator2

Ferry 目前提供两代生成器,完整说明见 docs/codegen.md 与 docs/codegen2.md。

v1:ferry_generator(built_value 路线)

生成的类基于built_value包,四大特性:

  1. 不可变:创建后无法修改
  2. 可比较:值相同的实例==相等
  3. 可序列化:内置toJson()/fromJson()
  4. Builder 模式:深度复制并修改字段

v1 额外支持多 Schema配置(schemas列表按目录划分作用域),适合复杂的中台架构。

v2:ferry_generator2(下一代,实验性)

ferry_generator2 主打小体积、快构建、纯 Dart 类,核心优势:

  • 🪶 无 builder、无序列化器样板代码,输出更小、编译更快
  • 🧱const构造函数 + 直接的toJson/fromJson
  • 🔒 接口/联集生成sealed 密封类,并附带__unknown兜底变体,模式匹配更安全
  • ⚙️copyWith==hashCodetoString全部可选配置
  • 🧩Fragment 复用去重:同一 Fragment 用于多个操作时只映射到一个类

从 v1 迁移到 v2 的详细步骤可参考 docs/migration-generator2.md。

进阶配置:这些长尾选项值得了解

枚举兜底值(防止新枚举值打崩客户端)

服务端新增枚举值而客户端未升级时,可配置 fallback 避免反序列化失败:

ferry_generator|graphql_builder: options: global_enum_fallbacks: true enum_fallbacks: MyEnumType: OTHER

自定义标量类型

自定义 scalar(如Date)可在build.yaml中映射为 Dart 类型并指定fromJson/toJson,更多用法见 docs/custom-scalars.md。

三态可选变量(Tristate Optionals)

开启tristate_optionals: true后,可空变量用Value<T>表示"未提供 / 显式 null / 有值"三种状态,专门解决"更新类 Mutation"中区分不更新置空的经典难题。

输出目录控制

默认输出到__generated__目录;设置output_dir: ''可让生成文件直接落在.graphql旁边。

常见问题 FAQ

Q1:为什么.graphql文件必须在lib/下?A:构建系统只能读取lib/内的资源,放到外面生成器无法访问。

Q2:构建时报 conflicting outputs 怎么办?A:给 build_runner 命令加上--delete-conflicting-outputs参数。

Q3:生成文件太多干扰浏览?A:在 VSCode 的settings.json中用files.exclude隐藏*.ast.gql.dart等模式(docs/codegen.md 提供了现成配置)。

Q4:v2 支持多 Schema 吗?A:目前 v2 仅支持单 Schema(schema.files会直接报错)。多 Schema 场景可拆分为多个 Dart 包、各配一个 Ferry 客户端,或继续使用 v1。

总结

Ferry 代码生成器把 "Schema → 强类型 Dart 类" 变成了一条命令的事:

  • 📥4 步上手:下载 Schema、写.graphql、配置build.yaml、跑 build_runner
  • 🛡️编译期安全:不可变、可比较、可序列化的强类型模型
  • 🆕两代可选:v1 稳定且支持多 Schema;v2 更轻更快,sealed 类型匹配体验极佳
  • 📚实战参考:端到端示例见 packages/ferry_generator2_end_to_end/ 与 examples/pokemon_explorer2/

现在就可以克隆仓库,给你的 GraphQL 项目加上 Ferry 强类型代码生成,让错误在编译期就无处遁形!

【免费下载链接】ferryStream-based strongly typed GraphQL client for Dart项目地址: https://gitcode.com/gh_mirrors/fer/ferry

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询