Codegen技术实战解析与前端自动化代码生成优化经验

UP主~秀丽 框架 阅读 794
赞 74 收藏
二维码
手机扫码查看
反馈

「我的写法,亲测靠谱」

Codegen 这玩意儿,我最早是被 GraphQL 客户端(比如 Apollo)带进来的,后来在微前端、BFF 层、甚至纯前端状态管理里都用上了。它不是银弹,但用对了真能省下大量样板代码——前提是别把它当黑盒瞎跑。

Codegen技术实战解析与前端自动化代码生成优化经验

我目前在三个中型项目里落地 Codegen,最稳的方案是:用 @graphql-codegen/cli + 自定义插件 + 本地脚本封装,不依赖 CI 自动触发,全部手动执行、人工 review 输出结果。为什么?因为一旦生成器出错,你连报错栈都找不到源头,全靠猜。

下面是我的标准工作流(已封装成 npm run gen):

npx graphql-codegen --config codegen.yml --watch=false && prettier --write 'src/generated/**/*.{ts,tsx}'

注意两个关键点:禁用 watch 模式(热重载容易漏文件、卡死、覆盖未提交改动),生成后立刻格式化(否则团队里有人用 Prettier、有人不用,diff 会疯掉)。

codegen.yml 的核心配置我也贴出来(删减了无关插件):

overwrite: true
schema:
  - 'https://jztheme.com/api/graphql'
  - './src/graphql/schema.graphql'
documents: './src/graphql/**/*.graphql'
generates:
  src/generated/graphql.ts:
    plugins:
      - typescript
      - typescript-operations
      - typescript-react-query
    config:
      skipTypename: true
      dedupeOperationSuffix: true
      exposeQueryKeys: true
      fetcher: ./src/lib/fetcher#fetcher
      reactQueryVersion: 5

这里重点说 exposeQueryKeysfetcher。前者让每个 query 自动生成 key 数组,方便手动 invalidate;后者指向我自己的 fetch 封装,里面统一处理 auth、超时、错误分类——千万别用默认 fetch! 我之前踩过坑:默认 fetch 不带 credentials,登录态丢了,接口全 401,查了俩小时才发现是 Codegen 自己配的 fetcher 覆盖了全局配置。

还有个细节:我强制把 schema 分成两份——线上地址用于 CI,本地 .graphql 文件用于开发联调。为什么?因为 dev 环境经常切 mock server 或本地 GraphQL 服务,如果只配一个线上地址,一断网就跑不动 gen,整个开发流程卡住。本地 schema 文件我用 graphql-inspector 做 diff 校验,有变更才提醒更新。

「这几种错误写法,别再踩坑了」

下面这些,都是我在 Codegen 上翻过的车,按严重程度排:

  • 错误1:直接 commit 生成文件到 Git,且没加 .gitattributes 或 ignore 注释
    后果:两个人改同一个 gql 文件,生成内容冲突,merge 后 query key 错乱,useQuery 报 undefined。我们试过自动 resolve,结果发现某些字段类型推导错位,runtime 崩溃。现在做法:所有 src/generated/**.gitignore,但文档里写清楚「必须本地运行 gen 后才能启动 dev server」。
  • 错误2:在 documents 里写 **/*.graphql,却不约束命名规范
    某次新加了个 user-profile-update.graphql,结果生成的 hook 叫 useUserProfileUpdateMutation,和已有 useUpdateUserProfileMutation 冲突,TS 直接报重定义。后来我们强制约定:query 必须以 Get 开头,mutation 必须以 Update/Create 开头,Codegen 配置加 dedupeOperationSuffix: true 才稳。
  • 错误3:把 Codegen 当 API 文档用,不写 @deprecated、不加 description
    后端改了个字段名,没标 deprecated,Codegen 照常生成新类型,老组件还在用旧字段,TS 不报错(因为类型宽泛),上线后 UI 空白。现在要求所有 gql 文件顶部加注释:# @deprecated use newField instead,并配插件自动校验字段弃用状态。
  • 错误4:在生成文件里手写逻辑,比如 patch 一个 type 或加个 helper 函数
    第一次改完,第二次 gen 直接覆盖。我说服团队用了「生成 + 手动扩展」模式:生成 graphql.ts,另建 graphql.ext.ts,里面用 declare module 补充类型,或导出包装函数。这样既不污染生成区,又保留灵活性。

「实际项目中的坑」

第一个真实场景:我们有个大表单页,包含 12 个子模块,每个模块有自己的 GraphQL 查询。Codegen 默认为每个 operation 单独生成 hook,导致页面 import 了 12 个 hook,bundle size 涨了 86KB。解决办法是用 typescript-react-query 插件的 groupOperations: true,把同域查询合并成一个 hook 文件,再配合 React.lazy 动态加载子模块——Codegen 本身不解决性能问题,但它得给你留出优化入口

第二个坑:TypeScript 版本升级后,types.d.ts 里生成的 Omit 类型报错。折腾半天发现是 Codegen 用了老版本的 TS 模板。最终方案不是降级 TS,而是加了一行 typescript: { disableTypescript: false } 并手动指定 typescript: 5.0(和项目一致)。记住:Codegen 的 TS 版本必须和项目 tsconfig.json 里的 compilerOptions.target 对齐,否则类型推导失真

第三个细节:我们曾把 Codegen 放进 husky pre-commit,结果 commit 一次等 12 秒,开发者直接绕过 lint。现在改成了 pre-push + 缓存检查:先比对 src/graphql/ 的 git hash 和上次生成的 hash,不一致才跑 gen。提速 90%,也避免了误触发。

「结尾唠叨两句」

以上是我总结的最佳实践,没有「完美方案」,只有「当前项目里最省心的方案」。Codegen 是工具,不是框架,它不会替你思考数据流设计、错误边界、loading 状态粒度——这些还得靠人。

如果你也在用 Codegen,欢迎评论区交流:你们怎么处理多环境 schema?有没有试过自定义插件做字段权限过滤?或者……你们还踩过哪些离谱的坑?

这个技巧的拓展用法还有很多,比如用 Codegen 生成 Zustand store 结构、生成 i18n key 类型、甚至生成 Jest mock 数据模板,后续我会继续分享这类博客。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论
利芹 Dev
读完这篇文章,我对技术的价值有了新的认识,不再只关注技术的难度。
点赞 15
2026-01-29 13:25