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

时间:2019-03-27 22:39:55

标签: vue.js vuejs2 vuex vue-router

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

users: {
    1234: { ... },
    46473: { name: 'Tom', topics: [345, 3456] }
},
userList: [46473, 1234]

我的问题是:当您的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'}, {...}],
    {...}
] 

为了示例,假设commentsusers的子模块:

选项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
},

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)
},

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

结论 你们如何应对这种情况和建议/最佳实践?答案非常感谢,因为我坚持这些事情。

此外,由于需求并不那么复杂,我也正在尝试避免使用3r方库,例如Vue ORM,normalizr等。

谢谢

PS:由于我只是在未经测试的情况下编写代码,所以该代码可能有错误,请着重注意。

1 个答案:

答案 0 :(得分:0)

为了避免在下面的状态中发生意外复杂性,在进行状态归一化时需要注意的重点。

如官方Redux docs

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

现在使用上面的示例,从数据中删除冗余。您可以将每个表用于每种信息,例如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"]
  }
}

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

更新后的答案

由于数据已按照组件进行规范化,因此只需执行一次操作就可以将父组件中的实体传递出去,并且作为规范化的一部分,可以实现以下好处。

  
      
  1. 更快地访问数据,不再需要遍历数组或嵌套对象。
  2.   
  3. 组件之间的松耦合。
  4.   
  5. 每个组件在商店中都有自己的位置,因此只有一个真相。
  6.   

希望这会有所帮助!