状态结构设计 #

设计原则 #

良好的状态结构设计是构建可维护 Vuex 应用的基础。

text
状态设计原则
├── 扁平化 ──────── 避免深层嵌套
├── 规范化 ──────── 消除数据冗余
├── 可预测 ──────── 状态变更可追踪
├── 可扩展 ──────── 便于添加新功能
└── 类型安全 ────── TypeScript 友好

扁平化设计 #

问题:深层嵌套 #

javascript
// 不推荐:深层嵌套结构
state: {
  app: {
    data: {
      users: {
        list: {
          items: [
            { id: 1, name: 'John' }
          ]
        }
      }
    }
  }
}

// 访问困难
const user = this.$store.state.app.data.users.list.items[0]

解决:扁平化结构 #

javascript
// 推荐:扁平化结构
state: {
  users: [
    { id: 1, name: 'John' }
  ]
}

// 访问简单
const user = this.$store.state.users[0]

规范化设计 #

问题:数据冗余 #

javascript
// 不推荐:数据冗余
state: {
  posts: [
    {
      id: 1,
      title: 'Post 1',
      author: { id: 1, name: 'John', email: 'john@example.com' }
    },
    {
      id: 2,
      title: 'Post 2',
      author: { id: 1, name: 'John', email: 'john@example.com' }  // 重复
    }
  ]
}

解决:规范化结构 #

javascript
// 推荐:规范化结构
state: {
  posts: {
    byId: {
      1: { id: 1, title: 'Post 1', authorId: 1 },
      2: { id: 2, title: 'Post 2', authorId: 1 }
    },
    allIds: [1, 2]
  },
  
  users: {
    byId: {
      1: { id: 1, name: 'John', email: 'john@example.com' }
    },
    allIds: [1]
  }
}

// Getters 中关联数据
getters: {
  postsWithAuthors: state => {
    return state.posts.allIds.map(id => {
      const post = state.posts.byId[id]
      return {
        ...post,
        author: state.users.byId[post.authorId]
      }
    })
  }
}

实体关系设计 #

一对多关系 #

javascript
// 用户与文章:一个用户有多篇文章
state: {
  users: {
    byId: {
      1: { id: 1, name: 'John' },
      2: { id: 2, name: 'Jane' }
    },
    allIds: [1, 2]
  },
  
  posts: {
    byId: {
      101: { id: 101, title: 'Post 1', authorId: 1 },
      102: { id: 102, title: 'Post 2', authorId: 1 },
      103: { id: 103, title: 'Post 3', authorId: 2 }
    },
    allIds: [101, 102, 103]
  }
}

getters: {
  // 获取用户的所有文章
  userPosts: state => userId => {
    return state.posts.allIds
      .map(id => state.posts.byId[id])
      .filter(post => post.authorId === userId)
  },
  
  // 获取文章作者
  postAuthor: state => postId => {
    const post = state.posts.byId[postId]
    return state.users.byId[post.authorId]
  }
}

多对多关系 #

javascript
// 文章与标签:多对多关系
state: {
  posts: {
    byId: {
      1: { id: 1, title: 'Post 1' },
      2: { id: 2, title: 'Post 2' }
    },
    allIds: [1, 2]
  },
  
  tags: {
    byId: {
      1: { id: 1, name: 'Vue' },
      2: { id: 2, name: 'React' },
      3: { id: 3, name: 'JavaScript' }
    },
    allIds: [1, 2, 3]
  },
  
  // 关系表
  postTags: [
    { postId: 1, tagId: 1 },
    { postId: 1, tagId: 3 },
    { postId: 2, tagId: 2 },
    { postId: 2, tagId: 3 }
  ]
}

getters: {
  // 获取文章的所有标签
  postTags: state => postId => {
    return state.postTags
      .filter(pt => pt.postId === postId)
      .map(pt => state.tags.byId[pt.tagId])
  },
  
  // 获取标签下的所有文章
  tagPosts: state => tagId => {
    return state.postTags
      .filter(pt => pt.tagId === tagId)
      .map(pt => state.posts.byId[pt.postId])
  }
}

按功能组织 #

模块化结构 #

javascript
// store/modules/user.js
export default {
  namespaced: true,
  
  state: {
    profile: null,
    preferences: {}
  },
  
  mutations: { /* ... */ },
  actions: { /* ... */ },
  getters: { /* ... */ }
}

// store/modules/products.js
export default {
  namespaced: true,
  
  state: {
    items: {},
    ids: [],
    categories: []
  },
  
  mutations: { /* ... */ },
  actions: { /* ... */ },
  getters: { /* ... */ }
}

// store/modules/cart.js
export default {
  namespaced: true,
  
  state: {
    items: [],
    total: 0
  },
  
  mutations: { /* ... */ },
  actions: { /* ... */ },
  getters: { /* ... */ }
}

UI 状态分离 #

javascript
state: {
  // 业务数据
  entities: {
    users: {},
    posts: {},
    comments: {}
  },
  
  // UI 状态
  ui: {
    loading: false,
    error: null,
    selectedUserId: null,
    filter: 'all'
  }
}

实战示例 #

电商应用状态设计 #

javascript
const store = createStore({
  state: {
    // 用户模块
    user: {
      profile: null,
      addresses: [],
      preferences: {}
    },
    
    // 产品模块
    products: {
      byId: {},
      allIds: [],
      categories: [],
      featured: []
    },
    
    // 购物车模块
    cart: {
      items: [],  // [{ productId, quantity, variant }]
      couponCode: null
    },
    
    // 订单模块
    orders: {
      byId: {},
      allIds: [],
      current: null
    },
    
    // UI 状态
    ui: {
      isLoading: false,
      error: null,
      toast: null,
      modal: null
    }
  },
  
  getters: {
    // 购物车商品详情
    cartItems: state => {
      return state.cart.items.map(item => ({
        ...state.products.byId[item.productId],
        quantity: item.quantity,
        variant: item.variant
      }))
    },
    
    // 购物车总价
    cartTotal: (state, getters) => {
      return getters.cartItems.reduce((total, item) => {
        return total + item.price * item.quantity
      }, 0)
    },
    
    // 购物车商品数量
    cartCount: state => {
      return state.cart.items.reduce((count, item) => {
        return count + item.quantity
      }, 0)
    }
  }
})

博客应用状态设计 #

javascript
const store = createStore({
  state: {
    // 文章
    posts: {
      byId: {},
      allIds: [],
      currentPage: 1,
      totalPages: 0
    },
    
    // 评论
    comments: {
      byId: {},
      byPostId: {}  // { postId: [commentId, ...] }
    },
    
    // 用户
    users: {
      byId: {},
      currentUserId: null
    },
    
    // 标签
    tags: {
      byId: {},
      allIds: []
    },
    
    // UI 状态
    ui: {
      loading: {
        posts: false,
        comments: false
      },
      error: null,
      activeTag: null,
      searchQuery: ''
    }
  },
  
  getters: {
    // 当前用户
    currentUser: state => {
      return state.users.byId[state.users.currentUserId]
    },
    
    // 文章列表
    postList: state => {
      return state.posts.allIds.map(id => state.posts.byId[id])
    },
    
    // 文章详情(含作者和评论)
    postDetail: state => postId => {
      const post = state.posts.byId[postId]
      if (!post) return null
      
      return {
        ...post,
        author: state.users.byId[post.authorId],
        comments: (state.comments.byPostId[postId] || [])
          .map(id => state.comments.byId[id])
      }
    },
    
    // 按标签筛选文章
    postsByTag: state => tagId => {
      return state.posts.allIds
        .map(id => state.posts.byId[id])
        .filter(post => post.tagIds.includes(tagId))
    }
  }
})

TypeScript 类型定义 #

typescript
// types/store.ts

// 用户类型
interface User {
  id: number
  name: string
  email: string
}

// 文章类型
interface Post {
  id: number
  title: string
  content: string
  authorId: number
  tagIds: number[]
  createdAt: string
}

// 评论类型
interface Comment {
  id: number
  postId: number
  userId: number
  content: string
  createdAt: string
}

// 规范化实体
interface EntityState<T> {
  byId: Record<number, T>
  allIds: number[]
}

// 根状态
interface RootState {
  users: EntityState<User>
  posts: EntityState<Post>
  comments: EntityState<Comment>
  
  ui: {
    loading: boolean
    error: string | null
  }
}

总结 #

状态结构设计的关键点:

设计原则 说明 示例
扁平化 避免深层嵌套 state.users 而非 state.app.data.users
规范化 消除数据冗余 使用 byIdallIds
模块化 按功能分割 userproductscart 模块
分离关注点 业务数据与 UI 状态分离 entitiesui 分开

继续学习 Getter基础,了解如何创建派生状态。

最后更新:2026-03-28