6.2 优化现场:从重建收窄到包体积


6.2 优化现场:从重建收窄到包体积

本节摘要:上一节会测量了,本节治病。按病灶清单推进:重建失控的三件套药方(const、watch 粒度、RepaintBoundary)、列表与图片两大高频场景、启动耗时与包体积的构成与瘦身。每一招都是"先测、再改、后验"的完整案例,全部出自轻记账的真实迭代。

重建失控与收窄三件套

最普遍的性能病灶是重建失控:一个开关的 setState 让整页重建,列表几千行陪着重跑 build。第 2 章讲过重建本身便宜,但"便宜的重建乘以几千"就是贵。药方三件套,按见效速度排序:

第一件:const 构造。 编译期常量 Widget 框架直接复用,父级重建它原地不动。零成本、零风险,能写就写:

// 标题永远不变:const 让它退出每次重建的行列 const Text('今日支出', style: TextStyle(fontSize: 14)), // 变化的只是数字本身:把变化圈在最小组件里 _TodayAmount(amount: state.total), // 只有这个小组件随金额重建

第二件:watch 粒度。 第 4 章的纪律在此量化:听小不听大。整页只 watch 一次"页面状态",拆成"金额、列表、筛选"三个小状态各自 watch,一个开关的变化惊动一个组件而不是一页组件。

第三件:RepaintBoundary。 它把重绘圈在边界内:边界内的重绘不波及外部,外部重绘也不波及它。列表项、动画区域、复杂图表的标准配置——第 3 章动画与图表两节埋的伏笔在此收线:

RepaintBoundary( child: CategoryPieCard(slices: slices), // 图表动效的重绘不波及整页 )

三件套的效果用数据说话:轻记账账单页改版前,点击筛选按钮触发整页重建约一百二十个组件;三件套落地后,同一操作重建范围缩到十一个组件,帧耗时从临界降到预算内。没有这次测量,三件套就只是"听说最佳实践";有了测量,它才是这次改版的验收结论。

图 15 一次点击的重建范围对比

图 15 一次点击的重建范围对比

列表与图片:两大高频场景

列表只一条纪律:长列表必须懒加载构造。ListView(children 全量构造)与 ListView.builder(滚动到哪构造到哪)的差异,在几百条数据时就是启动卡顿与否的分界。账单页的标准写法还差最后一块:itemExtent 给出行高提示,框架连"每行多高"都不用现算,滚动定位直接跳算:

ListView.builder( itemCount: bills.length, itemExtent: 64, // 行高固定时给提示,省一遍按需测量 itemBuilder: (context, i) { final bill = bills[i]; return RepaintBoundary( child: BillTile(key: ValueKey(bill.id), bill: bill), ); }, )

图片是内存与光栅的双重大户。三招按序上:给尺寸——Image.network 的 cacheWidth 或 cacheHeight 让解码尺寸匹配显示尺寸,一张四千像素原图贴在头像位上,解码内存能差出百倍;给占位——加载中显示占位图,布局稳定还免跳变;清单管理——发版前清一次未用图片资源,包体积与内存双收。

启动与包体积:构成与瘦身

启动耗时的主要构成是三段:引擎初始化(框架固定成本,动不了)、Dart 初始化(首帧前的全局初始化,main 里别干重活)、首帧渲染(首页别堆重图与复杂动效)。能动手的主要在后两段:main 里只做装配(第 4 章说过别在 main 同步等数据库);首屏按需加载,启动时只出骨架结构。

包体积的主要构成:引擎与运行时(固定底座,安卓按 ABI 分包后单包瘦身明显)、资源(图片是头号大户,能用矢量的不用位图,位图按实际显示分辨率给)、Dart 代码(AOT 产物,随功能增长,过期依赖清理见第 5.4 节)。发布期的构建命令本身就是第一道瘦身:

$ flutter build apk --split-per-abi # 按 CPU 架构分包,单包显著变小 $ flutter build appbundle # 上架用包,商店按设备下发对应产物 $ flutter analyze && flutter test # 出包前的最后防线(下一节的主角)

启动与体积的测量都要建立基线:改版前后各测一次同口径数据(冷启动到可交互的秒表时间、产物文件大小),写进发布检查单。性能的回归往往不是一次大错,而是十次"就多了这么一点"的累积——基线就是累积的刹车。

重建计数:让"谁在重建"自己开口

收窄三件套要打准,先得知道是谁在乱动。两个计数手段按场景选用:

粗粒度——build 里打日志。 在怀疑的组件 build 首行打印时间戳,操作一次界面数日志条数。上面的对比实验(一百二十对十一)最初就是靠这招拿到的。简单粗暴,适合"某次操作究竟惊动了多少组件"的总量判断。

细粒度——重建统计面板。 DevTools 的 Widget 重建统计页能按组件聚合重建次数,操作十秒,谁是重建大户一目了然。它还直接给出重建原因的排查线索:父级重建牵连、依赖的 InheritedWidget 变化、还是自身 setState。定位之后,对应的药方恰好就是三件套的其中一件——这个"统计定位、三件套对号"的配合是本节最值得带走的工作流。

一个实操提醒:计数本身有开销,统计面板只在排查时开,平时关掉——仪表是诊断用的,不是常驻的。

优化的时机学:什么时候不动手

收尾要泼一盆冷水:性能优化是有时机纪律的。三种情况明确别动手——功能还在原型期(结构随时推翻,优化成本打水漂);没有测量数据支撑("感觉会慢"不是证据);瓶颈不在被优化的地方(凭直觉猜热点,十个猜错九个)。正确时机是:Profile 测量证实了瓶颈、功能形态已稳定、优化收益可验证。把这页纪律贴在工位上,能省下项目里最贵的一类返工。

本节要点回顾

  • 收窄三件套:const 退出重建、watch 听小不听大、RepaintBoundary 圈住重绘;
  • 重建范围是设计出来的:改版前后一百二十对十一的差距来自最初的组件划分;
  • 长列表用 builder 加 itemExtent,图片给解码尺寸与占位,两处覆盖日常大头;
  • 启动优化抓 main 瘦身与首屏减负,体积优化抓分包构建、资源降采样与依赖清理;
  • 优化有时机纪律:没测量不动手、原型期不动手、瓶颈不明不动手。

性能关过了,正确性关呢?下一节架起三层测试防线,让前面的仓库、同步、界面全部进入自动化射程。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U