模組七|生態系與踩坑心得(Day 29–30)
先備:Day 21(CanvasKit 把畫面畫進 canvas)。這一篇不套三段式,因為它盤的是工具鏈,不是某個 API 的對照。
整個 UI 畫在一張 canvas 上,那 console.log 去哪了?
這是我開工前最擔心的問題。寫了十年 Vue,我的 debug 肌肉記憶全長在瀏覽器 DevTools 上:Elements 面板看結構、Console 戳狀態、Styles 面板即時改 CSS、Vue Devtools 看元件樹和 Pinia store。Flutter Web 把這一切畫進一個 <canvas>,直覺上等於把我的工具箱整個沒收。
結論先講:
比想像中好,但痛點在別處。
實測 28 天下來,我的 debug 工具箱沒有被沒收,是被換掉——換來的東西在開發期堪用,在 release build 那一關才真的開始痛。這篇把 debug 體驗和套件生態的坑一次盤完。
不過在盤之前,我得先誠實揭露這個專案的工具鏈現況,因為它會影響你怎麼看下面這些結論。
Flutter 端的 analysis_options.yaml 全檔是 flutter create 產生的樣板,關鍵在第 10 行:
# analysis_options.yaml:10
include: package:flutter_lints/flutter.yaml
linter:
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
官方推薦的 flutter_lints 有 include 進來,但底下 rules: 區塊每一條自訂規則都還是註解狀態——我一條都沒開、一條都沒關。也就是說,本系列反覆講的「編譯期防線」,我拿到的是官方預設值,沒有加碼。
測試更誠實一點:Flutter 端有一個 test/widget_test.dart(也是樣板產生的),Vue 端零個測試檔。
我把這兩件事放在最前面,是因為接下來我要說「Flutter 的工具鏈比想像中好」,而你有權知道我是在多低的標準上說這句話。
先確認座標系。Vue 的除錯體驗建立在一個前提上:你的 UI 就是 DOM,瀏覽器 DevTools 天生看得懂它。
console.log 想放哪放哪,source map 讓你點回 .ts 原始行號margin、color,滿意了再抄回 code——這是十年來我調版面的主要迴圈套件生態同理:npm 上任何一個前端套件,預設就是為瀏覽器而生,你幾乎不用問「這個套件支不支援 Web」。
三個層次,都直出瀏覽器 console:
import 'dart:developer' as developer;
import 'package:flutter/foundation.dart';
Future<void> connectQuoteStream() async {
// 1. 最粗暴:等同 console.log
print('connectQuoteStream: start');
// 2. 節流版:大量輸出時不會被平台丟棄,debug 專用
debugPrint('payload size: ${payload.length}');
// 3. 結構化:可帶分類名稱、error 物件、stack trace
try {
await ws.connect();
} catch (e, st) {
developer.log(
'websocket connect failed',
name: 'quotes.ws', // console 裡可用這個名稱過濾
error: e,
stackTrace: st,
);
}
}
更重要的是:debug 模式下 Dart 編譯成 JS 時帶 source map,console 裡的輸出和錯誤可以直接點回 .dart 的原始行號。跟 Vue 點回 .ts 的體驗幾乎一致。開工前最擔心的這一關,實際上是整篇最不痛的部分。
瀏覽器的 Elements 面板廢掉之後,補位的是 Flutter DevTools 的 Widget Inspector。flutter run -d chrome 起來後開 DevTools,Inspector 的 select mode 讓你點畫面上任何一塊,直接跳到 widget tree 對應節點,看它的 constraints、padding、實際 render 尺寸。體感上就是「Vue Devtools 的元件樹」而不是「Elements 面板」——它給你的是框架視角的結構,不是平台視角的 DOM。
其他照舊的:
.ts 下斷點沒有兩樣生態這邊的第一課:pub.dev 每個套件頁面都有平台支援標籤(Android / iOS / Web / ...),先看標籤再看 star 數。踩過的坑歸納成三類:
dart:io 依賴:任何碰檔案系統、原生 socket 的套件在 Web 上直接編譯失敗。好消息是失敗在編譯期,不是 runtime——你會在 flutter build web 就知道,而不是上線後web 通常能提前看到災情flutter.dev、dart.dev publisher)出品的可信度明顯高一級拿我這 28 天實際用到的類別盤點一輪:HTTP 與 WebSocket 官方等級套件穩定無虞;狀態管理(Riverpod、Bloc)本來就是純 Dart,跨平台零問題;圖表類選擇比 npm 少一個量級,但因為 Flutter 本來就是畫布,自己用 CustomPaint 補的成本反而低;i18n 與日期在地化堪用但排版細節要自己驗;最慘的是富文字編輯器這種深度依賴瀏覽器原生行為的類別,Web 上的成熟度跟 npm 生態完全不能比。
跟 npm「預設支援瀏覽器」的世界相比,這是心智模型要反過來的地方:Flutter 生態預設 mobile,Web 是要主動驗證的次要平台。選套件的順序從「找 star 最多的」變成「先過濾出 Web 標籤,再看 issue 區的 Web 災情,最後才比功能」。
<canvas>,右鍵檢查看不到任何結構。第一次做這件事的失落感很真實margin 看效果再抄回 code,只能改 Dart 靠 hot reload(web hot reload 自 Flutter 3.35 起預設啟用,3.44 更把 --web-hot-reload flag 標為 deprecated,這個秒級迴圈現在是穩的)。迴圈從「秒級、免切窗」變成「秒級、要切窗」,小但持續的摩擦補一個容易被忽略的正面項:效能分析反而不吃虧。Flutter DevTools 自帶 Performance 面板看 frame 耗時、rebuild 次數,對照 Vue 這邊要靠 Vue Devtools 的 profiler 加瀏覽器 Performance tab 拼湊,Flutter 的訊號反而更聚焦——因為整個渲染管線都是框架自己的,它對每一幀發生什麼事有完整的話語權。
這是我認定對 Vue 的真實劣勢,不是體感問題。
production 的 Flutter Web 是 minified JS(或 WASM),線上錯誤的 stack trace 近乎不可讀。要還原,得自行處理 source map 上傳——Sentry 有支援 Flutter Web,但整條鏈的成熟度、文件量、社群踩坑資料都輸 JS 生態一截。Vue 專案裡「上傳 source map 到錯誤監控」是 CI 裡一行外掛就解決的日常;Flutter Web 這邊你會花上明顯更多時間,而且 WASM 路線的工具鏈還在演進。
**線上事故的 debug 能力,是目前 Flutter Web 對 Vue 最實質的落差之一。**選型時如果你的產品對線上可觀測性要求高,這條要記進成本。
回扣本系列的獨家論點:我全程用 AI agent 開發,而 agent 的回饋迴圈吃的是 flutter analyze、編譯錯誤、test 輸出——它根本不靠 console 手戳,也不開 DevTools。
這代表上面列的「沒了的東西」,對 agent 工作流的傷害遠小於對人工開發的傷害。人類失去 Elements 面板會難受;agent 從來沒用過。反而 Dart 強型別讓錯誤集中在編譯期爆,正好餵進 agent 最擅長消化的訊號通道。人工 debug 的劣勢,被 agent 迴圈部分繞掉了——這是我在 Day 1 不敢肯定、28 天後可以肯定的事。
而這條迴圈在 Flutter 3.44 又往前推了一段:Agentic Hot Reload。Dart MCP server 會自動找到並連上正在執行的 Dart/Flutter app,agent 不需要任何手動設定,就能改完 code 直接觸發熱重載。同一版還把 MCP 的 tool 定義做了合併,agent 工作流的 token 成本跟著降,另外也強化了在 package 依賴裡搜尋的能力(不必開放整個本機 pub cache)。
意義不在「AI 會寫 code」——那件事早就成立——而在於 agent 的訊號來源第一次從編譯期擴張到執行期:它改完可以自己看正在跑的 app,再決定下一輪要改什麼。上面那句「agent 不開 DevTools」因此要補一個但書:它不開 DevTools,但它現在有另一條自己的管道連進 running app。這個轉變對前端比對 mobile 更有感,因為我們本來就活在瀏覽器的即時回饋裡。
當然「部分」兩個字要誠實:release build 可觀測性的痛,agent 繞不掉,線上事故終究是人要面對。Agentic Hot Reload 改善的是開發期迴圈,不是 production 的黑盒子。
一句話:開發期的 debug 體驗補得回來(Widget Inspector + source map + IDE 斷點),生態要習慣「Web 是次要平台」的預設,而 release 可觀測性是真實落差——但 agent 工作流讓前兩者的痛感大幅降級。
明天 Day 30,系列最後一篇。一個不信 Flutter Web 的 Vue 老手,30 天實測完的總結:學習地圖全回顧、Day 1 那棵決策樹要不要修、以及給 Vue 人的具體行動建議。
如果你卡在語法
深入原理