

子玥酱 (掘金 / 知乎 / CSDN / 简书 同名)
大家好,我是 子玥酱,一名长期深耕在一线的前端程序媛 👩💻。曾就职于多家知名互联网大厂,目前在某国企负责前端软件研发相关工作,主要聚焦于业务型系统的工程化建设与长期维护。
我持续输出和沉淀前端领域的实战经验,日常关注并分享的技术方向包括 前端工程化、小程序、React / RN、Flutter、跨端方案,
在复杂业务落地、组件抽象、性能优化以及多端协作方面积累了大量真实项目经验。
技术方向: 前端 / 跨端 / 小程序 / 移动端工程化 内容平台: 掘金、知乎、CSDN、简书 创作特点: 实战导向、源码拆解、少空谈多落地 **文章状态:**长期稳定更新,大量原创输出
我的内容主要围绕 前端技术实战、真实业务踩坑总结、框架与方案选型思考、行业趋势解读 展开。文章不会停留在"API 怎么用",而是更关注为什么这么设计、在什么场景下容易踩坑、真实项目中如何取舍,希望能帮你在实际工作中少走弯路。
子玥酱 · 前端成长记录官 ✨
👋 如果你正在做前端,或准备长期走前端这条路
📚 关注我,第一时间获取前端行业趋势与实践总结
🎁 可领取 11 类前端进阶学习资源 (工程化 / 框架 / 跨端 / 面试 / 架构)
💡 一起把技术学"明白",也用"到位"
持续写作,持续进阶。
愿我们都能在代码和生活里,走得更稳一点 🌱
文章目录
-
- 引言
- iOS:状态问题来得慢,但"动刀位置"非常清楚
-
- [一个很典型的 iOS 状态写法](#一个很典型的 iOS 状态写法)
- 当你发现状态设计有问题时
- [为什么 iOS 容忍度高?](#为什么 iOS 容忍度高?)
- RN:状态问题暴露早,但"全局依赖"让重构代价迅速上升
- [Flutter:状态问题暴露最快,但 UI 结构被"状态锁死"](#Flutter:状态问题暴露最快,但 UI 结构被“状态锁死”)
-
- [一个非常典型的 Flutter 全局状态](#一个非常典型的 Flutter 全局状态)
- 真正的问题出现在"时间之后"
- 这时你想重构状态
- [Flutter 的"不可逆性"来自哪里?](#Flutter 的“不可逆性”来自哪里?)
- 一个直观对比:同样的"状态拆分"成本
- [为什么 Flutter 团队更依赖"早期共识"?](#为什么 Flutter 团队更依赖“早期共识”?)
-
- [真正提高 Flutter 状态"容错率"的手段只有这些](#真正提高 Flutter 状态“容错率”的手段只有这些)
- 最后的结论(站在工程角度)
引言
几乎每个做过中大型项目的人,都会在某个时刻意识到:
这个状态设计,好像一开始就不该这么搞。
但真正决定项目命运的,不是你有没有意识到问题,而是:
你用的技术栈,还给不给你"推倒重来"的空间。
从代码结构的角度看,Flutter、RN、iOS 对状态重构的容忍度,差异非常明显。
iOS:状态问题来得慢,但"动刀位置"非常清楚
一个很典型的 iOS 状态写法
swift
class ProfileViewController: UIViewController {
private let viewModel = ProfileViewModel()
override func viewDidLoad() {
super.viewDidLoad()
bind()
}
func bind() {
viewModel.onUserChanged = { [weak self] user in
self?.updateUI(user)
}
}
}
swift
class ProfileViewModel {
var user: User? {
didSet {
onUserChanged?(user)
}
}
var onUserChanged: ((User?) -> Void)?
}
这里哪怕设计得不完美,也有几个天然特征:
- 状态挂在 对象 上
- 生命周期 = ViewController 生命周期
- UI 更新路径是显式的
当你发现状态设计有问题时
比如你意识到:
User太大了- Profile 页面不该依赖完整 User
- 只需要展示态数据
重构的第一步通常是:
swift
struct ProfileViewState {
let name: String
let avatar: URL
}
然后改动范围非常明确:
- 改 ViewModel
- 改 ViewController
- 影响面基本止步于当前模块
为什么 iOS 容忍度高?
不是因为设计更优雅,而是因为:
- 跨 VC 共享状态成本高
- 对象边界天然存在
- UI 和状态不是同一个结构层级
这使得 错误状态很难自然扩散。
RN:状态问题暴露早,但"全局依赖"让重构代价迅速上升
RN 中非常真实的一段 Redux / Zustand 状态
ts
export const useUserStore = create(set => ({
user: null,
setUser: user => set({ user }),
}));
页面里用得非常舒服:
tsx
const user = useUserStore(state => state.user);
一开始看起来非常干净。
当项目变大之后
你可能会看到:
tsx
const isVip = useUserStore(state => state.user?.vip);
const canEdit = useUserStore(state => state.user?.permissions.includes('edit'));
状态开始被 切片消费。
这时你意识到问题了
比如你想做一次"正确的重构":
- 拆分 user
- 区分 authUser / profileUser
- 限制 UI 直接依赖
理论上你想做的是:
ts
const useAuthStore = ...
const useProfileStore = ...
但现实中你要面对的是:
- 十几个组件同时依赖 user
- useEffect 副作用散落各处
- render 逻辑强依赖字段结构
你改的不是一个 store,而是:
一整片组件渲染路径。
RN 的状态为什么"难改"?
因为:
- 状态和 render 逻辑写在一起
- hooks 隐含依赖关系
- 全局状态一旦成型,很难逐步迁移
结果就是:
- 新状态加在旁边
- 老状态慢慢"别碰"
- 技术债以并行方式增长
Flutter:状态问题暴露最快,但 UI 结构被"状态锁死"
一个非常典型的 Flutter 全局状态
dart
final userProvider = StateProvider<User?>((ref) => null);
页面中:
dart
@override
Widget build(BuildContext context, WidgetRef ref) {
final user = ref.watch(userProvider);
if (user == null) {
return const LoginView();
}
if (user.vip) {
return VipHome();
}
return NormalHome();
}
这段代码本身并不"错"。
真正的问题出现在"时间之后"
随着需求增长,你会慢慢看到:
dart
if (user.vip && user.permissions.contains('edit')) {
...
}
if (user.profileCompleted) {
...
}
Widget 树结构,已经深度依赖 user 的语义。
这时你想重构状态
比如你意识到:
- user 承担了太多语义
- UI 不该直接依赖完整 User
- 需要拆分 ViewState
你理论上想要的是:
dart
final homeViewStateProvider = Provider<HomeViewState>((ref) {
final user = ref.watch(userProvider);
return HomeViewState.fromUser(user);
});
但现实是:
- UI 分支写在 build 里
- Widget 拆分基于旧状态
- rebuild 范围和结构已经绑定
你要改的不是 Provider,而是:
Widget 树本身。
Flutter 的"不可逆性"来自哪里?
因为 Flutter 的核心模型是:
UI = f(state)
一旦 state 成为 UI 结构的"控制变量":
- 改状态 = 改 UI
- 拆状态 = 拆 Widget
- 调整语义 = 重组 build 逻辑
这不是简单重构,而是结构迁移。
一个直观对比:同样的"状态拆分"成本
iOS
- 改 ViewModel
- 改 ViewController
- UI 结构基本不动
RN
- 改 store
- 改 hooks
- 多个组件 render 路径调整
Flutter
- 改 Provider
- 改 watch 关系
- 重组 Widget 树
- 重新控制 rebuild 范围
为什么 Flutter 团队更依赖"早期共识"?
很多 Flutter 团队在状态出问题时,第一反应是:
- 换 Riverpod
- 上 Bloc
- 重写状态层
但你会发现:
dart
ref.watch(...)
已经写在 太多 Widget 里了。
工具换了,但 UI 结构没变,问题不会消失。
真正提高 Flutter 状态"容错率"的手段只有这些
dart
// 1. 页面状态默认私有
class PageState {
int tabIndex = 0;
}
// 2. 全局只放"事实",不放"判断"
final currentUserProvider = Provider<User?>((ref) => ...);
// 3. UI 判断在页面层完成
这不是"优雅",而是提前止损。
最后的结论(站在工程角度)
状态重构,本质是和时间赛跑。
- iOS:慢一点,但还找得到刀口
- RN:发现早,但全局依赖让你迟疑
- Flutter:问题立刻显形,但回头路最陡
所以你会看到:
成熟的 Flutter 项目,几乎都在避免"状态重构",而不是依赖它。
它们真正做对的不是:
- 用了多复杂的状态管理方案
而是:
- 早期极度克制
- 极少全局
- 对"状态提升"异常谨慎