在这里插入图片描述
在这里插入图片描述

子玥酱 (掘金 / 知乎 / CSDN / 简书 同名)

大家好,我是 子玥酱,一名长期深耕在一线的前端程序媛 👩‍💻。曾就职于多家知名互联网大厂,目前在某国企负责前端软件研发相关工作,主要聚焦于业务型系统的工程化建设与长期维护。

我持续输出和沉淀前端领域的实战经验,日常关注并分享的技术方向包括 前端工程化、小程序、React / RN、Flutter、跨端方案
在复杂业务落地、组件抽象、性能优化以及多端协作方面积累了大量真实项目经验。

技术方向:前端 / 跨端 / 小程序 / 移动端工程化
内容平台:
掘金、知乎、CSDN、简书
创作特点:
实战导向、源码拆解、少空谈多落地
文章状态:
长期稳定更新,大量原创输出

我的内容主要围绕 前端技术实战、真实业务踩坑总结、框架与方案选型思考、行业趋势解读 展开。文章不会停留在“API 怎么用”,而是更关注为什么这么设计、在什么场景下容易踩坑、真实项目中如何取舍,希望能帮你在实际工作中少走弯路。

子玥酱 · 前端成长记录官 ✨
👋 如果你正在做前端,或准备长期走前端这条路
📚 关注我,第一时间获取前端行业趋势与实践总结
🎁 可领取 11 类前端进阶学习资源(工程化 / 框架 / 跨端 / 面试 / 架构)
💡 一起把技术学“明白”,也用“到位”

持续写作,持续进阶。
愿我们都能在代码和生活里,走得更稳一点 🌱

引言

几乎没有哪个 Flutter 项目,一开始就是卡的。

第一个版本通常很美好:

  • 页面少
  • 状态清晰
  • 列表丝滑
  • 动画流畅

甚至你会觉得:Flutter 这玩意儿,真香。

但两年之后,你再打开同一个项目,会发现很多问题都不是“突然出现”的,而是一步一步被允许发生的

为了快,把一切都写进 build 里

项目早期,节奏往往很快。

  • 需求赶
  • 人手少
  • 重构没时间

于是很多代码,看起来“先跑起来再说”。


Widget build(BuildContext context) {
  final user = context.watch<UserProvider>().user;
  final config = context.watch<ConfigProvider>().config;

  final items = buildItems(user, config); // 动态生成

  return ListView(
    children: items.map((e) => ItemWidget(e)).toList(),
  );
}

在当时:

  • 数据量小
  • Provider 变更少
  • 滚动不频繁

一切看起来都没问题。

但从这一刻开始,性能的第一颗种子已经埋下了

build 不再是“描述 UI”,而是“做计算”。

状态开始“顺手往上提”

随着页面变多,组件复用需求出现。

于是状态开始上移:

  • 子组件不方便管理 → 提到父组件
  • 父组件还不够 → 提到页面
  • 页面还不够 → 提到全局 Provider
ChangeNotifierProvider(
  create: (_) => PageState(),
  child: Page(),
);

这一步在语义上没有错。

真正的问题在于:没人再去定义 rebuild 的边界。

最终你会得到:

  • 一个页面级 Provider
  • 控制着十几个子组件
  • 任意字段变化,整个页面重建

当页面开始变复杂,列表开始变长,卡顿就开始出现了。

列表“看起来还能用”,但已经开始抖了

常见表现

  • 快速滑动时掉帧
  • 偶发卡顿,很难复现
  • Debug 看着还行,Release 也不稳

但因为:

  • 不是必现
  • 不影响主流程
  • 优先级永远排不过需求

所以它会被一句话带过:

“Flutter 列表本来就这样吧。”

而实际上,此时列表已经具备几个典型特征:

  • 外层被 setState 包住
  • renderItem 依赖外部 Provider
  • key 使用不规范

性能债开始累计,但没人愿意碰。

为了“修”,引入更多机制

当卡顿开始被用户感知,团队开始尝试“优化”。

常见操作包括:

  • 拆 Widget
  • 加 Consumer
  • 用 Selector
  • 给 ListItem 加 AutomaticKeepAlive

这些手段本身没有错。

但问题是:它们是在一个已经失控的结构上打补丁。

结果往往是:

  • rebuild 路径更难理解
  • keepAlive 滥用,内存上涨
  • 某些列表项永远不释放

性能问题暂时缓解,但系统复杂度进一步上升。

没人再敢动“那一块代码”

到了中后期,项目里一定会出现几块区域:

  • “这里很容易卡,别动”
  • “这个列表逻辑很复杂”
  • “改了可能会炸”

这些区域往往具备几个特征:

  • 多层 Provider 嵌套
  • build 方法极长
  • 状态来源不清晰

此时,性能问题已经从技术问题,变成了组织问题

没人有足够信心和时间去重构它。

Flutter 开始“背锅”

当性能问题频繁出现,但又找不到明确责任点时,讨论通常会变成:

  • Flutter 不成熟
  • 自绘 UI 本来就吃性能
  • 框架不如原生稳定

但如果你冷静回头看,会发现:

框架并没有变慢,是项目的结构逐渐失控了。

Flutter 把很多“性能边界”交给了开发者:

  • rebuild 范围
  • 状态粒度
  • 列表惰性程度

而一旦这些边界被模糊,性能退化是必然的

为什么这种失控在 Flutter 项目里更常见?

不是因为 Flutter 更差,而是因为:

  • UI 和逻辑高度耦合
  • build 写法太“顺手”
  • 没有系统级兜底限制

在 iOS 里,你很难:

  • 一次刷新整个 TableView
  • 随手改一个字段导致全屏重绘

在 Flutter 里,这些都不会被阻止。

性能不是“优化”出来的

大型 Flutter 项目的性能,往往不是靠后期优化救回来的,而是:

  • 早期有没有划清 rebuild 边界
  • 状态是不是按层级设计
  • 列表是否从一开始就按“长期使用”写

如果一开始就把 build 当成“逻辑容器”,那项目越大,性能失控只是时间问题。

总结

几乎每一个变慢的 Flutter 项目,都不是突然崩的。

它们都经历了:

  • 为了快而妥协
  • 为了复用而上移状态
  • 为了修补而堆机制
  • 为了稳定而不敢重构

性能不是一行代码的问题,而是每一次“先这样吧”累积起来的结果

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐