15 扁平化你的 Store,合理使用持久化
一句话理解
Store(Pinia / Vuex)里的数据结构要像数据库表一样扁平 (用 id 做关联,别深层嵌套);持久化(存 localStorage)要有选择,别什么都存。
为什么
- 深层嵌套的 Store → 更新困难、响应式开销大(呼应第 07 条)。比如要改"文章 5 的评论 3 的点赞数",深层路径又慢又容易写错。
- 扁平化(normalized) → 用
{ [id]: item }的 map 结构,查找 O(1),更新精准。 - 持久化全存 → localStorage 有容量限制(5MB 左右),存太多会卡、还会存进敏感数据。
代码示例
反例:嵌套 + 全量持久化:
js
// 嵌套结构:改一个评论的点赞数要穿透 3 层
const badStore = {
posts: [
{
id: 1,
title: '...',
comments: [
{ id: 10, likes: 5, author: { id: 7, name: '张三' } }
]
}
]
}
正例:扁平化(像数据库表):
js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useForumStore = defineStore('forum', () => {
// 三张"表",用 id 关联,互不嵌套
const posts = ref({}) // { [postId]: post }
const comments = ref({}) // { [commentId]: comment }
const users = ref({}) // { [userId]: user }
// O(1) 查找某篇文章
const getPost = (id) => posts.value[id]
// 精准更新某条评论的点赞数,不影响其它数据
const likeComment = (commentId) => {
comments.value[commentId].likes++
}
return { posts, comments, users, getPost, likeComment }
})
有选择地持久化(只存该存的):
js
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useUserStore = defineStore('user', () => {
const token = ref('') // 持久化:登录态
const profile = ref({}) // 持久化:用户资料
const tempCaptcha = ref('') // 不持久化:临时验证码
const formDraft = ref({}) // 不持久化:表单草稿
// 手动持久化关键数据(或用 pinia-plugin-persistedstate 按需配置)
function persist() {
localStorage.setItem('token', token.value)
}
return { token, profile, tempCaptcha, formDraft, persist }
})
</script>
新人提示
- 扁平化的核心思想:数据按"实体"分表,用 id 互相引用,和后端数据库设计同源。
- 持久化的判断:"刷新页面后还需要吗?" 需要(如登录态)→ 存;不需要(如临时状态)→ 不存。
- 别持久化敏感数据(密码、密钥)到 localStorage,它对任何 JS 都开放。