如何在 Vuex 中实现状态规范化?

Hel*_*cas 7 vue.js vue-router vuex vuejs2

我正在开发一个也使用 Vuex 的应用程序,只是遇到了处理嵌套对象会很痛苦的情况,所以我试图尽可能地规范化(展平)状态,如下例所示:

users: {
    1234: { ... },
    46473: { name: 'Tom', topics: [345, 3456] }
},
userList: [46473, 1234]
Run Code Online (Sandbox Code Playgroud)

我的问题是:当您的 API 响应如下所示时,实现上述目标的“最佳”方法什么

data: [
    {id: 'u_0001', name: 'John', coments: [{id: 'c_001', body: 'Lorem Ipsum'}, {...}],
    {id: 'u_0002', name: 'Jane', coments: [{id: 'c_002', body: 'Lorem Ipsum'}, {...}],
    {...}
] 
Run Code Online (Sandbox Code Playgroud)

为了示例的缘故,假设它comments是 的子模块users

选项1:

// action on the user module
export const users = ({ commit, state }, users) => {
    commit(SET_USERS, users)
    commit('comments/SET_COMMENTS', users)
}


// mutation on the user module
[types.SET_USERS] (state, users) {
     state.users = users.reduce((obj, user) => {
         obj[user.id] = {
             id: user.id, 
             name: user.name,
             comments: user.comments.map(comment => comment.id)
         } 
         return obj
     }, {})
    state.userIds = users.map(user => user.id)
},


// mutation on the comments module
[types.SET_COMMENTS] (state, users) {
    let allComments = []
    users.forEach(user =>  {
        let comments = user.comments.reduce((obj, comment) => {
            obj[comment.id] = comment
            return obj
        }, {})
        allComments.push(comments)
    })

   state.comments = ...allComments
},
Run Code Online (Sandbox Code Playgroud)

IMO 这个选项很好,因为你不必担心每次更改页面时重置状态(SPA/Vue-Router),避免由于某种原因id: u_001不再存在的情况,因为每次更改时状态都会被覆盖调用,但将 传递users array给两个突变感觉很奇怪。

选项 2:

// action on the user module
export const users = ({ commit, state }, users) => {
    // Here you would have to reset the state first (I think)
    // commit(RESET)

    users.forEach(user => {
        commit(SET_USER, user)
        commit('comments/SET_COMMENTS', user.comments)
    })
}


// mutation on the user module
[types.SET_USER] (state, user) {
    state.users[user.id] = {
        id: user.id, 
        name: user.name,
        comments: user.comments.map(comment => comment.id)
    }  
    state.userIds.push(user.id)
},


// mutation on the comments module
[types.SET_COMMENTS] (state, comments) {
    comments.forEach(comment => {
        Vue.set(state.comments, comment.id, comment)
    })
    state.commentsIds.push(...comments.map(comment => comment.id)
},
Run Code Online (Sandbox Code Playgroud)

在这种情况下,需要重置状态,否则每次离开并重新租用页面时都会有重复/旧值。Wich 有点烦人,而且对错误或不一致的行为更有利。

结论 你们如何处理这些场景和建议/最佳实践?由于我坚持这些事情,因此非常感谢答案。

此外,我试图避免使用 Vue ORM、normalizr 等 3r 方库,因为需求并不那么复杂。

谢谢,

PS:代码可能有错误,因为我没有测试就写了,请关注大局。

Var*_*tel 4

那么,为了避免状态中的意外复杂性,下面是在进行状态标准化时需要注意的几点。

正如 Redux 官方文档中提到的

  1. 每种类型的数据在状态中都有自己的“表”。
  2. 每个“数据表”应将各个项目存储在一个对象中,其中项目的 ID 作为键,项目本身作为值。
  3. 对单个项目的任何引用都应通过存储项目的 ID 来完成。
  4. ID 数组应该用于指示顺序。

现在以上面的示例为例,删除数据中的冗余。您可以将每个表用于每个信息,例如userscomments等等。

{
  'users': {
    byId : {
      "user1" : {
        username : "user1",
        name : "User 1",
      },
      "user2" : {
        username : "user2",
        name : "User 2",
      },
      ...
    },
    allIds : ["user1", "user2", ..]
  },
  'comments': {
    byId : {
      "comment1" : {
        id : "comment1",
        author : "user2",
        body: 'Lorem Ipsum'
      },
      "comment2" : {
        id : "comment2",
        author : "user3",
        body: 'Lorem Ipsum'
    },
    allIds : ["comment1", "comment2"]
  }
}
Run Code Online (Sandbox Code Playgroud)

通过这样做,我们可以确保更多的组件被连接并负责查找和维护自己的数据集,而不是每个组件都有一个大的数据集并将数据传递给子组件。

更新的答案

由于数据已根据组件进行标准化,因此通过单个操作传递来自父组件的实体并作为标准化的一部分,可以实现以下好处。

  1. 更快的数据访问,不再迭代数组或嵌套对象。
  2. 组件之间的松耦合。
  3. 每个组件在存储中都有自己的位置,因此存在单一事实点。

希望这可以帮助!