Vuex中如何实现状态归一化?
How to achieve state normalization in Vuex?
我正在开发一个也使用 Vuex 的应用程序,只是遇到了处理嵌套对象会很痛苦的情况,所以我尝试尽可能地规范化(展平)状态,如下例所示:
users: {
1234: { ... },
46473: { name: 'Tom', topics: [345, 3456] }
},
userList: [46473, 1234]
我的问题是:当您的 API 响应如下所示时,"best" 实现上述目标的方法是什么:
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'}, {...}],
{...}
]
假设 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
},
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)
},
在这种情况下,需要重置状态,否则每次离开并重新登录页面时都会有 repeated/old 值。这有点烦人,更容易出现错误或不一致的行为。
结论
你们如何处理此类情况和 advices/best 做法?非常感谢回答,因为我坚持这些事情。
此外,我正在尝试避免像 Vue ORM、normalizr 等 3r 方库,因为需求并不那么复杂。
谢谢,
PS: 代码是自己写的,没有测试,可能有错误,请看大局。
嗯,为了避免下面状态的意外复杂性,是在进行状态归一化时需要注意的要点。
官方Redux下提到docs
- Each type of data gets its own "table" in the state.
- Each "data table" should store the individual items in an object, with the IDs of the items as keys and the items themselves as the values.
- Any references to individual items should be done by storing the item's ID.
- Arrays of IDs should be used to indicate ordering.
现在用上面的例子,从数据中去除冗余。您可以使用每个 table 来表示每个信息,例如 users
、comments
等等。
{
'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"]
}
}
通过这样做,我们可以确保更多的组件连接起来并负责查找和维护自己的数据集,而不是每个组件都有一个大数据集并将数据传递给子组件。
更新的答案
由于数据已根据组件规范化,通过单个操作从父组件传递实体并作为规范化的一部分,可以获得以下好处。
- Faster data access, no more iterating over arrays or nested objects.
- Loose coupling between components.
- Each component has its own place in the store, hence there is a single point of truth.
希望对您有所帮助!
我正在开发一个也使用 Vuex 的应用程序,只是遇到了处理嵌套对象会很痛苦的情况,所以我尝试尽可能地规范化(展平)状态,如下例所示:
users: {
1234: { ... },
46473: { name: 'Tom', topics: [345, 3456] }
},
userList: [46473, 1234]
我的问题是:当您的 API 响应如下所示时,"best" 实现上述目标的方法是什么:
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'}, {...}],
{...}
]
假设 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
},
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)
},
在这种情况下,需要重置状态,否则每次离开并重新登录页面时都会有 repeated/old 值。这有点烦人,更容易出现错误或不一致的行为。
结论 你们如何处理此类情况和 advices/best 做法?非常感谢回答,因为我坚持这些事情。
此外,我正在尝试避免像 Vue ORM、normalizr 等 3r 方库,因为需求并不那么复杂。
谢谢,
PS: 代码是自己写的,没有测试,可能有错误,请看大局。
嗯,为了避免下面状态的意外复杂性,是在进行状态归一化时需要注意的要点。
官方Redux下提到docs
- Each type of data gets its own "table" in the state.
- Each "data table" should store the individual items in an object, with the IDs of the items as keys and the items themselves as the values.
- Any references to individual items should be done by storing the item's ID.
- Arrays of IDs should be used to indicate ordering.
现在用上面的例子,从数据中去除冗余。您可以使用每个 table 来表示每个信息,例如 users
、comments
等等。
{
'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"]
}
}
通过这样做,我们可以确保更多的组件连接起来并负责查找和维护自己的数据集,而不是每个组件都有一个大数据集并将数据传递给子组件。
更新的答案
由于数据已根据组件规范化,通过单个操作从父组件传递实体并作为规范化的一部分,可以获得以下好处。
- Faster data access, no more iterating over arrays or nested objects.
- Loose coupling between components.
- Each component has its own place in the store, hence there is a single point of truth.
希望对您有所帮助!