ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Flutter Web首屏性能优化:从构建到部署的全链路实践

Flutter Web首屏性能优化:从构建到部署的全链路实践 1. 问题拆解Flutter for web 首次加载为什么这么慢1.1 先认清 Flutter Web 的组成做 Flutter Web 优化第一件事不是改代码而是把构建产物吃透。很多人上来就搜首次加载优化盲目加 CDN、开压缩其实连build/web里那几个文件是干嘛的都没搞清楚优化自然打不到点子上。执行一次flutter build web --release之后输出目录大致是index.html入口页面Flutter 引擎初始化从这里开始。flutter_bootstrap.js新版本里负责引导整个应用的加载脚本旧版本的flutter.js也还在用。main.dart.js你所有 Dart 代码编译后的产物整个业务逻辑全在这里面通常也是体积大头。main.dart.js.mapSource Map线上环境根本不需要默认不会带。canvaskit/CanvasKit 渲染引擎的 WASM 文件和 JS 胶水代码负责把你的 UI 画到浏览器 Canvas 上。assets/图片、字体、Manifest 里声明的 Assets 资源。manifest.jsonPWA 配置文件用于离线缓存和安装到桌面。icons/PWA 图标。version.json版本信息Service Worker 更新时会去对比。明白这些文件的分工你才能理解后续所有优化动作。比如canvaskit目录里的.wasm文件动辄 1MB 以上它和main.dart.js通常就是首屏最耗时的两个大头。1.2 三个拖慢首次加载的元凶我在开发阶段第一次部署 Flutter Web 时用无痕浏览器打开页面白屏时间接近 8 秒当时的构建配置还是 debug 模式那场面简直灾难。后来做了 Release 构建降到了 4 秒左右依然不够理想。拆开 Network 面板看问题集中在三个环节。下载体积过大。Release 构建下一个中等复杂度的业务项目main.dart.js依然可能达到 900KB 到 1.5MBCanvasKit 的 WASM 又要占 1MB 左右。加上字体、图片、Assets首屏需要通过网络拉取的总数据量轻轻松松超过 3MB。这在移动端网络环境下就是个灾难。解析与执行阻塞渲染。Dart 代码被编译成了 JavaScript浏览器要先把 JS 下载下来解析成 AST再编译成字节码最后执行。这段过程是阻塞型任务执行期间主线程没法渲染页面所以你会看到白屏。Flutter Web 的启动是一个约 3MB 左右的 JS 脚本在浏览器主线程上完成大量初始化工作时间开销非常明显。CanvasKit 引擎初始化。Flutter Web 默认使用 CanvasKit 渲染引擎它依赖 WebGL 把 Flutter 的 UI 绘制到浏览器上。WASM 文件下载完成后还需要初始化 GPU 上下文、加载字体这个过程本身就要额外消耗一两秒。在低端设备上Shader 编译还会带来额外的卡顿。1.3 判断你的卡顿属于哪种类型优化之前先打开浏览器 DevTools 的 Performance 面板记录一次完整的页面刷新用 Network 面板看看如果首字节时间很长说明网络链路和服务器响应有问题重点查部署和 CDN。如果JS 文件下载时间很久说明体积太大是主因重点做构建产物瘦身。如果下载很快但页面渲染延迟了 1 秒以上说明瓶颈在执行和引擎初始化上重点做加载策略和体验优化。如果CPU 长时间跑满且发生在绘制阶段说明需要考虑渲染器类型切换或代码内部性能问题。这里还要提一句flutter isolate概念。Flutter Web 的工程代码默认跑在同一个 isolate 上你不能像移动端那样随意开后台 isolate 做计算。如果你在业务里用了Isolate.run这种写法在 Web 端最终会被编译成 Web Worker但受浏览器限制数据传递有额外序列化开销。优化时不要把它当万能药反而可能让首屏更慢。我自己习惯的做法是先跑一个空项目部署上线测出当前网络和设备环境下的基础耗时然后再部署真实业务项目两相对比就能知道哪些时间是 Flutter 框架本身无法避免的哪些是自己代码和资源导致的。这一步做完后续优化的方向就很清晰了。2. 构建端优化从源头把包做小2.1 构建模式与渲染器的选择先把最基础的一件事说透线上部署必须用 Release 模式。这是 Flutter Web 性能的分水岭。flutter run --release或flutter build web --release会开启压缩、Tree Shaking、代码混淆如果显式开启产物体积和执行效率都会大幅提升。Debug 模式下各种断言、类型检查、热重载辅助代码全在里面体积大五六倍很正常。然后是渲染器。Flutter 3.16 之前有一个--web-renderer参数可以切换canvaskit或html两种渲染器。HTML 渲染器体积小、加载快但绘制效果和浏览器兼容性不稳定只适合对 UI 要求不高的场景。CanvaKit 是默认选择跨平台效果一致但需要加载 WASM。到了 Flutter 3.29 之后又引入了skwasm作为可选的 WebAssembly 渲染器在 Chrome 系浏览器上性能更好、产物更小但兼容范围有限。选型的逻辑很简单如果你要兼容 Safari、Firefox 等浏览器直接默认 CanvaKit如果你只面向 Chrome 内核且 Flutter 版本支持 skwasm可以尝试切换并验证视觉效果。不管选哪个记住别为了省体积去用已被废弃的 HTML 渲染器Visual 一致性损失较高后续维护也会很麻烦。2.2 用 --dart-define 做功能裁剪和场景开关在实际项目里一个很大的浪费点在于测试环境专用的代码、调试面板、日志上报逻辑全部被编译进了生产包。这些东西是不可见的却实实在在占体积。我习惯用--dart-define在构建时注入环境变量。比如const String apiBaseUrl String.fromEnvironment(API_BASE_URL, defaultValue: https://api.example.com); const bool enableDebugPanel bool.fromEnvironment(DEBUG_PANEL, defaultValue: false);然后在构建命令里按需指定flutter build web --release --dart-defineAPI_BASE_URLhttps://api.example.com --dart-defineDEBUG_PANELfalse因为String.fromEnvironment和bool.fromEnvironment是编译期常量当enableDebugPanel被定为false时对应的if (enableDebugPanel) { ... }代码块在 Tree Shaking 阶段会被整个移除不会进入最终产物。这是个编译期剪裁机制比运行时判断来得更彻底。同样地如果你的项目里集成了多种上报 SDK埋点、崩溃、性能监控建议给它们统一封装一层并根据环境做条件编译。这样生产环境不引入开发者调试工具首屏体积能进一步缩小。2.3 延迟加载不着急用的页面就别塞进首包Flutter Web 和移动端一样支持deferred import延迟加载这是最有效的代码级瘦身手段之一。原理是把某些模块的代码单独拆分到独立的 JS chunk 中首屏加载完主包之后进入对应页面时才按需请求对应 chunk。使用方式是在 Dart 里给 import 加deferred as关键字import package:app/pages/admin_page.dart deferred as admin; // 在使用之前先加载 await admin.loadLibrary();然后构建时Flutter 会自动为admin_page.dart相关代码生成一个独立的 JS fragment 文件只有在loadLibrary()被调用时才会去下载和执行。我踩过的坑是把deferred用在了过于频繁切换的页面上。比如某个 Tab 页用户进入概率超过 80%延迟加载反而会带来不必要的等待。正确的用法是低频功能后台管理、协议弹窗、帮助中心、大图表页面用延迟加载高频主路径保持同步加载。另外注意deferred目前不能和部分代码生成工具如json_serializable生成的.g.dart文件混用因为生成代码可能没有正确导入。团队里如果用了这个方案建议在 CI 流程里加一个flutter build web --release构建校验防止有人提交无法编译的代码。2.4 第三方依赖瘦身很多时候首包体积膨胀不是你自己写的代码而是依赖了一堆庞然大物。比如dio网络库本身不大但你再引入dio_cache_interceptor、pretty_dio_logger、web_socket_channel、encrypt等一连串库体积就上来了。检查依赖体积的办法比较朴素先看pubspec.lock别让版本解析引入奇怪传递依赖再用flutter build web --release构建后看main.dart.js体积变化做二分排查还可以试一下flutter pub deps命令查看依赖树。常见的替代方案日期时间类库如果只用DateTime.parse和简单的格式化完全没必要引入intl手写几个函数搞定能省出一大截体积。图片加载cached_network_image虽然方便但会额外携带缓存策略、占位图处理等逻辑。如果页面简单直接用Image.network加上一个自己实现的简单缓存即可。状态管理provider、riverpod这类轻量方案在 Web 场景下比bloc的样板代码和代码量更友好。UI 组件库flutter_screenutil适配库在 Web 上价值有限响应式布局用官方LayoutBuilder、MediaQuery就足够了。还有个大坑是 Google Fonts。如果你用了google_fonts包首次运行时会尝试从 Google 的 CDN 拉取字体不仅慢而且在某些网络环境下直接请求失败导致字体回退闪烁。最好下载字体文件放在 assets 里本地加载这在 Web 端非常关键。2.5 构建产物分析工具使用心得单纯靠猜是没法判断哪些代码占了体积的。Flutter 官方推荐的工具是app_size分析器可以这样用flutter build web --release --analyze-size构建结束后控制台会输出一个链接打开后能看到main.dart.js中每个库/包的体积占比还能看到 tree-shaking 前后的大小差异。我在实际项目中通过这个工具发现某个图表库的日期处理依赖占了主包体积的 18%。后来我换成了轻量自绘方案主包直接从 1.2MB 降到 950KB 左右。另外build/web目录里有个main.dart.js文件直接看体积最直观。如果你发现体积异常大优先排查是不是把开发期的 debug 代码、Print 日志、测试 mock 数据打进去了。3. 部署端优化用缓存和压缩把加载时间拉下来3.1 Nginx 静态托管与 Brotli/Gzip 压缩构建产物准备好之后部署策略直接影响首屏感知。如果只是简单地把文件丢到 Nginx 里默认情况下main.dart.js以原始体积传输浪费了很多带宽。正确的姿势是在 Nginx 里开启压缩。先说 gzip这是最通用的方案gzip on; gzip_comp_level 6; gzip_min_length 1k; gzip_types text/plain text/css application/javascript application/json application/wasm image/svgxml;注意 WASM 文件也要加上application/wasm否则不会压缩。但 gzip 对 JS 的压缩率有限Brotli 效果更好一般能比 gzip 再小 15% 到 25%。Nginx 需要在编译时启用--with-http_v2_module和--with-http_brotli_module不同发行版安装方式略有差异。启用后配置大概长这样brotli on; brotli_comp_level 6; brotli_min_length 1k; brotli_types text/plain text/css application/javascript application/json application/wasm image/svgxml;实际操作建议同时开启 gzip 和 brotliNginx 会根据请求头的Accept-Encoding自动选择浏览器支持的压缩算法。我部署之后main.dart.js从 1MB 左右降到 280KB 左右首屏下载时间砍掉了近一半效果立竿见影。3.2 缓存策略什么该缓存什么不该缓存压缩解决的是传输体积问题缓存解决的是重复访问问题。Flutter Web 的静态资源可以按类型区分缓存策略。index.html刷新入口不能长缓存建议使用no-cache并让 Service Worker 和浏览器始终重新验证。因为版本更新时main.dart.js的文件名会变index.html里引用的资源路径也会跟着变。如果缓存了旧 HTML用户会一直访问旧版本。main.dart.js、canvaskit/*.wasm、assets/*这些文件名默认带有版本 hashAssets 路径结构里含版本号可以放心加长缓存。一个基本的 Nginx 配置示例location / { root /var/www/flutter_app; index index.html; try_files $uri $uri/ /index.html; } location /assets { expires 1y; add_header Cache-Control public, immutable; } location /canvaskit { expires 1y; add_header Cache-Control public, immutable; } location /index.html { expires -1; add_header Cache-Control no-cache, no-store, must-revalidate; }如果你用 Nginx 同时部署多个 Web 项目比如/app1和/app2各挂一个 Flutter 应用那么location块就按子路径区分并注意 Flutter 构建时加--base-href/app1/来让资源引用路径正确。3.3 Service Worker 与 PWA 策略取舍Flutter Web 构建时会生成 Service Workerflutter_service_worker.js默认策略是缓存优先。这套机制的目标是第一次访问时拉取资源并缓存之后应用可以离线运行。但你可能会遇到一个经典问题——版本更新后用户看到的还是旧版本。原因在于 Service Worker 更新机制浏览器检测到 Service Worker 文件有变化会下载新的 SW 并进入 waiting 状态旧的页面仍然由旧 SW 控制直到用户关闭所有标签页再重新打开新 SW 才会接管。这在 Flutter Web 上表现尤其明显用户刷新页面发现内容没变然后各种吐槽怎么不更新。解决办法有三种思路构建时指定--pwa-strategynone不使用 Service Worker。适合产品形态偏工具型、不需要离线能力的场景。指定--pwa-strategyoffline-first保留离线支持但在 Nginx 层面对flutter_service_worker.js设置Cache-Control: no-cache确保浏览器每次都能拿到最新 SW 文件。在应用内做版本检查发现新版本后弹窗提示刷新。这个方案最稳妥实现也简单请求一个/version.json或者之前构建产物的version.json对比本地缓存的版本号。我实际项目里选择了第二种 一种非阻塞式版本检查。也就是保留离线能力同时每次启动静默检查version.json有新版本时用Notification或 UI 小标签提示用户刷新。体验和更新及时性都能兼顾。3.4 CDN 与边缘节点对于用户分布在不同地区的场景单机 Nginx 即使压缩和缓存做得再好跨地域的网络延迟依然明显。这时需要 CDN 兜底。Flutter Web 的产物都是纯静态文件CDN 部署没有什么成本唯一要注意的是CDN 需要正确识别 Service Worker 的 MIME Type。比如.js必须是application/javascript.wasm必须是application/wasm否则某些浏览器会拒绝执行 Service Worker 或 WASM。如果你用的是云厂商 CDN源站填 Nginx 服务器地址并设置一个合理的缓存 TTL。为避免版本更新后 CDN 缓存旧文件Flutter Web 的main.dart.js文件名里有版本 hash所以它的缓存时间可以设置很长1 年而index.html和flutter_service_worker.js必须回源获取或者至少用短 TTL。还有一点很多团队在开发环境用flutter run -d web-server启动临时服务线上却用 NginxCDN这两者的服务能力差异很大。建议在 CI 流程里把flutter build web --release的产物直接作为部署产物不要本地构建后手动上传避免开发环境和线上环境产物不一致的问题。4. 首屏体验优化加载过程中的用户感知4.1 自定义 loading 占位页面技术优化做得再彻底首次加载的绝对耗时也很难进入秒开区间。Flutter 引擎需要下载并执行脚本、初始化渲染器这个窗口期用户看到的是空白页面。体验上最直观的改善就是让页面在 Flutter 启动之前先展示一个静态的 HTML 布局。Flutter 构建出来的index.html默认是空白的但你可以直接改它。在body里加入一个容器和简单的 CSS 动画body div idloading div classloader/div p正在加载应用.../p /div script srcflutter_bootstrap.js async/script script _flutter.loader.loadEntrypoint({ onEntrypointLoaded: async function(engineInitializer) { // 消除 loading 标识的时机 document.getElementById(loading).style.display none; let appRunner await engineInitializer.initializeEngine(); appRunner.runApp(); } }); /script /body要留意的是默认构建的index.html如果被 Flutter 工具重新生成自定义内容会被覆盖。比较稳妥的做法是单独维护一份web/index.html模板构建命令会基于这一份来生成最终产物。我习惯在所有 Flutter Web 项目里都放一份自己定制的index.html既控制 loading 效果也顺便配置 CSP内容安全策略基础项。4.2 加载骨架与字体预加载如果只是转圈动画体验提升有限。更进阶的做法是把首屏的关键框架先用纯 HTML/CSS 画出来比如顶部导航栏、侧边栏的占位色块、标题文字的区域。这样用户刷新后 100ms 内就能看到页面结构心理感知上会快很多。这个骨架屏方案听起来简单做的时候要注意骨架屏的结构要尽量接近 Flutter 首屏布局但不要做到像素级一致否则代码维护成本会很高。我的做法是只画大块面左右布局、卡片形状、一行标题Flutter 起来后自然覆盖掉。字体预加载也是一个容易被忽略的点。Flutter 引擎本身内置了 Material 字体但如果你的页面用了自定义字体建议在index.html的head里用link relpreload提前加载link relpreload href/assets/fonts/MyFont.woff2 asfont typefont/woff2 crossorigin这能让字体的下载和引擎初始化并行进行避免 Flutter 渲染完成时卡在字体加载上。4.3 降低首帧时间从优化启动流程做起Flutter Web 的启动过程是下载 JS → 解析执行 → 初始化引擎 → 渲染首帧。你可以通过监听首帧事件来量化耗时void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { override Widget build(BuildContext context) { // 监听首帧可以用 SchedulerBinding.addPostFrameCallback // 完成计时并上报 return MaterialApp(...); } }实测经验告诉我启动阶段如果主工程在main()里做了太多同步操作比如初始化数据库、预登录、加载本地配置首帧时间会显著变长。正确的做法是把必要的初始化延迟到第一个页面 build 之后用Future异步执行或者把逻辑放在SchedulerBinding.instance.addPostFrameCallback里。更深的优化思路是把runApp的入口分包首帧只渲染一个简单的登录/首页壳子业务模块在网络请求返回后再填充数据。这样 Flutter 能更快画出第一帧用户至少看到界面了后续加载业务内容时再用进度指示器兜底。4.4 网络请求与渲染并行很多 Flutter Web 应用的瓶颈并不在 Flutter 引擎本身而在业务代码的串行逻辑。比如打开首页先runApp→initState里请求用户信息 → 拿到数据后再请求菜单列表 → 最后渲染整个页面。这个链路如果每个请求 200ms用户就要白等 600ms。我在项目里把这类流程改成先渲染页面框架同时并行发起所有必要的网络请求数据到达后逐个局部刷新。Dart 的Future.wait可以直接实现并行请求final results await Future.wait([ fetchUserInfo(), fetchMenuList(), fetchAnnouncement(), ]);这种做法配合骨架屏用户体验会比等所有数据就绪再画页面好非常多。Flutter Web 的 UI 更新和网络请求天然在同一个 isolate 里异步机制不会阻塞渲染并行请求是完全安全的选择。5. 常见问题与排查实录5.1 unable to find suitable visual studio toolc 之类的本地构建报错Flutter Web 和桌面端共用不少工具链有时候你只是做 Web 开发也会触发这类和本地工具链相关的报错。比如 unable to find suitable visual studio toolchain多发生在flutter pub get或构建某个依赖时因为包里有 plugin 的原生代码声明需要本地编译环境。遇到这种情况先冷静区分如果是纯 Dart 包理论上不需要原生工具链。如果某个第三方包同时支持 Web 和 mobile它可能在pubspec.yaml的plugin里声明了原生实现Flutter 工具为了完整性会尝试查找本地工具链。解决办法一般是从环境变量或路径配置入手或者卸载掉并不使用的桌面端依赖。我的建议是用 FVM 固定 Flutter 版本并保持团队环境一致避免本地版本和 CI 版本不一致导致我本地能跑线上构建却炸了的尴尬。5.2 could not register service worker: InvalidStateError这是 Flutter Web 用户在部署时最常碰到的问题之一。报错信息往往出现在控制台Error: Could not register service worker: InvalidStateError常见触发场景是本地用flutter run打开应用时出现或者部署的路径和构建时不一致。用自定义域名 非根目录部署时尤其容易触发。原因基本是Service Worker 的注册路径和当前页面作用域不一致。浏览器要求 SW 脚本位于同源路径下且作用域范围不能超过脚本目录。如果你把 Flutter 应用部署在/app子路径但构建时没有指定--base-href/app/flutter_service_worker.js的注册作用域就会指向/这在子路径下就是非法的。解决办法flutter build web --release --base-href/app/然后确保 Nginx 配置里/app能正确映射到构建目录。如果你用的是根路径部署检查服务器配置里有没有把flutter_service_worker.js拦截或重定向到别处。还有可能是你手动修改过的index.html里注册 SW 的代码和 Flutter 引导代码冲突查一下flutter_bootstrap.js加载逻辑是否被改动。5.3 开发环境正常线上首屏却差距巨大我排查过不少这类问题原因集中在开发环境用了flutter run -d web-server本地回环网络延迟低资源从本机读取感知不到体积问题。线上环境 Nginx 没开压缩或者 CDN 源站配置错误导致资源二次回源。Production 构建里误带了--debug参数。解决思路是在线上环境用 DevTools 的 Network 面板重新做一次完整瀑布流分析对比开发环境和线上环境的请求耗时差异。没有开压缩是最常见的往往一开 Brotli整个首屏速度瞬间提升 40% 以上。5.4 多版本 Flutter 管理与构建环境统一热词里有 fvm安装多版本flutter这确实是搞 Flutter Web 团队应该重视的事。不同 Flutter 版本的构建产物策略差异很大特别是渲染器和 Service Worker 行为3.7、3.16、3.29 这几个版本之间Web 产物结构变化明显。我用 FVM 的典型工作流是fvm install 3.24.0 fvm use 3.24.0 fvm flutter pub get fvm flutter build web --release --base-href/app/在 CI 里也用fvm flutter执行构建保证全链路版本一致。这能避免很多奇怪的问题比如某同事用旧版本构建出老的flutter_bootstrap.js导致线上部署和新版 Nginx 配置不兼容。5.5 内存优化与 Web 端的特殊性Flutter Web 场景的内存问题和移动端不同。浏览器给单个标签页的内存有限如果在 Flutter 里持有大量图片缓存、长列表数据不释放很容易触发浏览器崩溃或页面无响应。排查方法是在浏览器任务管理器里观察内存曲线配合 DevTools 的 Memory 面板做堆快照对比。实际项目里我做的内存优化手段包括图片列表懒加载用VisibilityDetector或自己监听滚动位置只在图片进入视口时才解码。及时清理缓存列表翻页时释放不可见页面的图片缓存。避免大量 Stream 订阅不取消这在 Web 端容易造成内存持续增长。低端设备降低 Canvas 复杂度比如减少阴影、模糊效果的使用因为 WebGL 在低端 GPU 上渲染这些效果非常吃力。还有个小细节image_picker、camera这类插件在 Web 端的实现和移动端有差异有些操作会触发浏览器文件选择器或媒体权限弹窗。如果业务里有相关功能测试时一定不要只在桌面浏览器点一遍要实际用移动端浏览器走一遍流程。5.6 请求报错 dsh web authentication required很多 Flutter 开发者是从手机端转过来的在命令行里跑flutter run启动 Web 时见过类似 dsh web authentication required; reopen the url printed by dsh web 的提示。这个报错大多出现在某些开发服务器缺省了凭据或浏览器缓存了旧的认证状态时。和线上首屏优化关系不大但容易干扰调试我说一下排查方向如果你是直接flutter run -d chrome默认打开本机调试服务一般是不会触发认证的。如果你把flutter run挂到远程开发机上就需要通过web-server方式启动再用浏览器访问打印出来的 URL必要时加上--web-hostname和--web-port参数。如果遇到认证提示先清理浏览器该端口的 Cookie 和 Service Worker 缓存再看是否用了错误的代理环境。这类报错只是开发工具链问题不影响生产环境但会浪费大量时间排查我先写在这个板块里方便你少走弯路。5.7 常见问题速查表问题现象可能原因处理建议首屏白屏 5 秒以上未开启 Release 构建 / 服务器未开启压缩确认flutter build web --release开启 Brotli/gzip更新后用户仍看到旧页面Service Worker 缓存了旧版本Nginx 对flutter_service_worker.js设置no-cache或使用--pwa-strategynonemain.dart.js体积超过 1.5MB未做树摇/依赖过重/业务都在首包使用--analyze-size定位延迟加载低频模块裁剪第三方库子路径部署时资源 404未指定--base-href构建时加--base-href/app/Service Worker 注册报 InvalidStateErrorSW 作用域和部署路径不一致检查 base-href、服务器映射、SW 脚本 MIME 类型首页图片加载闪烁Google Fonts 或大图资源未预加载字体放入 assets图片使用视口内懒加载桌面浏览器正常、手机浏览器崩溃内存占用过高图片懒加载、列表缓存清理、降低过度绘制CDN 部署后加载反而慢CDN 未回源正确或 MIME 类型不对检查源站配置和.wasm的 Content-Type写在最后做 Flutter Web 首屏优化不是一锤子买卖每次升级 Flutter 版本、引入新依赖、增加业务模块体积和加载策略都可能发生变化。我在实际项目里的习惯是CI 里构建完直接输出产物体积的机器可读数据并设置一个阈值比如main.dart.js超过 1.2MB 就警告、超过 1.5MB 就失败强制团队关注体积问题。这个方法简单粗暴但确实能避免不知不觉就膨胀了的情况。另外很多优化手段是需要组合使用的。单开 Brotli 不压缩 WASM效果就打折只做代码分包不优化启动逻辑首帧时间一样很长。我建议你先跑一轮基准测试记录优化前的数据总下载体积、FCP、首帧可交互时间然后按构建端→部署端→体验端的顺序逐项优化每做一项改动都重新测一次直到达成目标。最后再分享一个小技巧Flutter Web 的调试面板可以在浏览器里按Shift 右键打开快速性能菜单能够直接看到 FPS 变化。如果你把整个优化流程走完了这个数字会给你一个非常直观的回报感。
返回列表