![]()
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.