![]()
7次网络请求,下载了几十KB数据,用户只看到3个字段。这不是故障,是REST的日常。
2012年,Facebook工程师在2G网络下刷News Feed时,被这个问题逼到掀桌。他们给出的解法叫GraphQL——不是让服务器决定给你什么,而是客户端开口要,服务器精准给,一趟搞定。
12年过去,Flutter开发者仍在重复当年的困境。这篇手册不是教程复读,而是拆解graphql_flutter这个库的设计逻辑:缓存为什么必须做归一化、生产级架构怎么搭、什么时候该用什么时候该跑。
REST的隐藏税:你付了多少带宽冤枉钱
想象你去餐厅点菜,服务员每次端上一整桌宴席,不管你只要一碗面。这就是Over-fetching——/users/42返回20个字段,你UI只用了3个。
更糟的是Under-fetching。要显示用户头像、最新5条帖子、每条帖子的点赞数,REST让你先查用户,再查帖子列表,再循环查每条帖子的点赞。7次往返,用户盯着转圈。
版本号是另一个债务。API v1字段不敢删,v2字段不敢加,客户端和服务器在兼容性里互相绑架。Facebook当年没有耐心等世界进化,他们直接换了游戏规则。
GraphQL的核心交易:一个端点,客户端定义数据结构。服务器只负责执行,不负责猜测。
Schema:比文档更硬的契约
GraphQL的Schema是强类型的、可 introspection(自省)的、运行时校验的。它不像Swagger那样靠人维护,而是服务器直接暴露类型系统,客户端工具能自动生成代码、自动补全、自动校验。
类型系统里,Scalar是原子(String/Int/Boolean/ID/Float),Object是复合结构,Interface和Union处理多态,Enum限定取值范围。这套设计不是为了学术美感,是为了让前后端在编译期就能发现断裂,而不是生产环境炸给用户看。
Query读数据,Mutation写数据,Subscription推实时更新。三种操作共享同一套类型系统,同一套端点,同一套授权逻辑。没有GET/POST/PATCH的语义分裂,没有URL设计的宗教战争。
graphql_flutter:缓存是主角,查询是配角
flutter_graphql这个库的设计哲学很直白:网络请求是昂贵的,必须被缓存拦截。它的Normalized Cache(归一化缓存)不是简单存JSON字符串,而是把响应拆成对象图,按类型+ID索引。
这意味着什么?你第一次查用户A和他的帖子,缓存存的是User:A和Post:1、Post:2、Post:3的独立记录。第二次查用户B,如果他也发了Post:3,缓存直接命中,不会重复请求。
归一化缓存解决的是数据一致性问题。同一条帖子在Feed里、在搜索里、在个人主页里,永远是同一个对象。改一处,全局刷新。
代价是配置复杂度。你要教缓存怎么识别对象身份(typePolicies),怎么处理分页(fetchMore的合并逻辑),怎么在Mutation后自动更新相关Query(update回调或refetchQueries)。这些不是bug,是GraphQL把数据所有权从服务器移交到客户端后,客户端必须承担的责任。
生产架构:怎么组织不会烂尾
小项目可以把Query Widget堆在UI层,但项目膨胀后,你需要三层隔离:数据层(GraphQLClient配置、缓存策略、错误处理)、仓库层(按业务域封装的查询/变更方法)、UI层(只消费,不直接碰客户端)。
错误处理要分网络错误、GraphQL错误(业务逻辑返回的errors数组)、部分成功(有data也有errors)。graphql_flutter的QueryResult把这些混在一起,你需要自己包装一层语义清晰的Result类型。
订阅(Subscription)在Flutter里走WebSocket,生命周期要和Widget绑定,断线重连、认证令牌刷新、后台切回后的状态恢复,都是坑。不是不能用,是要算清楚维护成本。
2012年Facebook的那批工程师,如果看到今天的Flutter开发者仍在为7次请求优化,会是什么表情?
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.