Как добиться нормализации состояния в Vuex?

#vue.js #vuejs2 #vuex #vue-router

#vue.js #vuejs2 #vuex #vue-маршрутизатор

Вопрос:

Я разрабатываю приложение, также использующее 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'}, {...}],
    {...}
] 
  

Предполагая ради примера, что 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)
},
  

В этой ситуации необходимо сбросить состояние, иначе у вас будут повторяющиеся / старые значения каждый раз, когда вы покидаете страницу и повторно арендуете ее. Что немного раздражает и более подвержено ошибкам или непоследовательному поведению.

Заключение Как вы, ребята, справляетесь с такими сценариями и советами / лучшими практиками? Ответы очень ценятся, поскольку я застрял на этих вещах.

Кроме того, я пытаюсь избегать сторонних библиотек 3r, таких как Vue ORM, normalizr и т.д., Потому что потребности не такие сложные.

Спасибо,

PS: В коде могут быть ошибки, поскольку я только что написал его без тестирования, пожалуйста, сосредоточьтесь на общей картине.

Комментарии:

1. Мне нравится вариант № 1, но это только мое мнение. Есть ли у вас реальная проблема с любым подходом, с которым вам нужна помощь?

2. В настоящее время я реализовал 2-й вариант, затем осознал необходимость сброса состояния, вероятно, в любой ситуации, когда есть вложенные данные из API, поэтому я создал мутацию на корневом уровне, которая получает два аргумента: строку с именем модуля (например: ‘users’) и состояние по умолчанию для этого модуля в виде функции, перебирайте его, например: for (const key in defaults) { Vue.set(state[module], key, defaults[key]) } Суть в том, что доступ к вложенным модулям становится очень сложным, и мутация СБРОСА в каждом модуле не очень эффективна.

Ответ №1:

Что ж, чтобы избежать случайного усложнения состояния, ниже приведены моменты, на которые необходимо обратить внимание при выполнении нормализации состояния.

Как упоминалось в официальных документах Redux

  1. Каждый тип данных получает свою собственную «таблицу» в состоянии.
  2. Каждая «таблица данных» должна хранить отдельные элементы в объекте, с идентификаторами элементов в качестве ключей и самими элементами в качестве значений.
  3. Любые ссылки на отдельные элементы должны выполняться путем сохранения идентификатора элемента.
  4. Для указания порядка следует использовать массивы идентификаторов.

Теперь с приведенным выше примером, чтобы удалить избыточность из данных. Вы можете использовать каждую таблицу для каждой информации, такой как 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"]
  }
}
  

Поступая таким образом, мы можем убедиться, что подключено больше компонентов, которые отвечают за поиск и поддержание своего собственного набора данных, вместо того, чтобы каждый компонент имел большой набор данных и передавал данные детскому компоненту.

ОБНОВЛЕННЫЙ ОТВЕТ

Поскольку данные были нормализованы в соответствии с компонентом, передайте объекты из родительского компонента одним действием и в рамках нормализации могут быть достигнуты следующие преимущества.

  1. Более быстрый доступ к данным, больше никаких итераций по массивам или вложенным объектам.
  2. Слабая связь между компонентами.
  3. У каждого компонента есть свое собственное место в хранилище, следовательно, существует единственная точка истины.

Надеюсь, это поможет!

Комментарии:

1. Спасибо, но ваш ответ больше подошел бы для: есть ли какой-либо способ избежать вложенных данных в vuex. Советы по нормализации я уже знаю. Мой вопрос — следующий шаг в том, как на самом деле это сделать. Представьте, что у вас есть что-то вроде: пользователи> комментарии> теги (вложенные данные) в вашем пользовательском модуле action вы бы запускали 3 мутации, всегда передавая массив users и внутри каждого цикла, пока не достигнете желаемых данных, или вы бы сначала перебирали пользователей, запускали мутацию для установки каждого пользователя, затем одну мутацию для установки всех комментариев и, наконец, перебирали комментарии и запускали мутацию для установки всех тегов?

2. Обновленный ответ. с удовольствием добавлю, если потребуется больше разъяснений.