扁平化数据结构通过将嵌套树形状态转为按ID索引的表形结构,显著提升读取速度、更新精度和订阅效率;它避免深克隆、减少冗余渲染、消除数据不一致,并支持O(1)查找,配合normalizr/@pinia/norm、分实体模块和记忆化selector效果更佳。

扁平化数据结构(Normalization)是优化大型 Redux 或 Pinia 状态树最直接有效的手段之一。它不靠压缩体积,而是通过重构数据组织方式,让读取更快、更新更准、组件订阅更轻量。
为什么嵌套结构在大型应用里容易拖慢性能?
当状态像这样层层嵌套:
{ users: [{ id: 1, name: 'Alice', posts: [{ id: 101, title: 'Hello', comments: [...] }] }] }
问题会集中爆发:
- 修改某条评论时,需深克隆整个用户→帖子→评论链,开销随嵌套深度指数增长
- 多个组件分别订阅
users[0].posts和users[0].posts[0].comments,一次更新可能触发多次无关重渲染 - 同一份数据(如某个 user 对象)在不同位置重复出现,更新一处漏掉另一处,导致视图不一致
- 无法用 ID 快速查到任意实体,每次都要遍历数组,O(n) 查找成本高
规范化后的标准结构长什么样?
核心是把“树形”变“表形”,按实体类型分表存储,用 ID 做键:
{ entities: { users: { byId: { '1': { id: 1, name: 'Alice' } }, allIds: [1] }, posts: { byId: { '101': { id: 101, userId: 1, title: 'Hello' } }, allIds: [101] }, comments: { byId: { '201': { id: 201, postId: 101, text: 'Nice!' } }, allIds: [201] } }, result: [101] // 比如搜索结果只存 ID 列表 }
这种结构类似数据库:每个实体类型独立建表,关联靠外键(userId、postId),不再靠嵌套引用。
如何落地规范化?三步走
不用从头手写,工具链已很成熟:
- 接入 normalizr(Redux)或 @pinia/norm(Pinia):API 风格统一,只需定义 schema,后端返回的嵌套 JSON 一键转为扁平结构
-
reducer / store 模块按 entity 切分:比如
users.ts只管 users 表的增删改查,posts.ts只管 posts 表,逻辑解耦清晰 -
组件内用 selector 封装查询逻辑:例如
useUser(1)内部自动从entities.users.byId拿数据,不暴露底层结构
配套必须做的两件事
光扁平化还不够,要配齐“搭档”才能发挥最大效果:
- 用 Reselect(Redux)或 computed + shallowRef(Pinia)做记忆化选择器:避免每次 render 都重新拼接嵌套视图(如“用户+其所有文章+每篇文章的作者名”)
-
配合不可变更新工具:Redux Toolkit 的
createEntityAdapter、Pinia 的defineStore+$patch都原生支持 ID 精准更新,避免深拷贝

















