Интерпретация исходного кода: некоторые дефекты Vuex

внешний интерфейс Flux Vue.js Vuex

Как мы все знаем,VuexдаFluxРеализация архитектуры. Flux четко устанавливает различные функциональные блоки в сценарии управления данными, и его основные принципы таковы:

  1. Централизованное государственное управление
  2. Статус может быть только специальным突变единица измерения
  3. Прикладной уровень инициирует изменения, отправляя сигналы (обычно называемые действиями).

Vuex также разработан с учетом этих рекомендаций.storeКласс обеспечивает основные функции шаблона Flux. В дополнение к удовлетворению основных требований архитектуры дополнительно разработаны многие удобные меры:

  1. Изолируйте блоки данных с помощью «модульной» конструкции
  2. Обеспечьте механизм получения для улучшения возможности повторного использования кода.
  3. использоватьVue.$watchметод реализации потока данных
  4. Нулевая конфигурация, естественно интегрированная в среду Vue

Аналитических статей в интернете уже много, так что повторяться нет нужды. Только эта статьяЦентрализация, сигнальный механизм, поток данныхРаскрывается реализация трех пунктов, обсуждаются дефекты реализации Vuex.

Центр

В Vuex,storeОн объединяет все функции, является основным внешним интерфейсом, а также центром управления данными в режиме Flux. Через него Vuex в основном обеспечивает:

  • Связанные с сигналом:dispatch、commit
  • Интерфейс слушателя:subscribe
  • Интерфейс изменения значения состояния (заменяет значение состояния, не должен вызываться):replaceState
  • интерфейс изменения модели состояния (рекомендуется использовать только в эталонных сценариях по запросу):registerModule、unregisterModule
  • Интерфейс горячего обновления (HMRЛогика, не обращая внимания):hotUpdate

официально реализованоstoreОчень сложный и в сочетании с большим количеством логики. Для краткости мы удалим все виды обходной логики и сосредоточимся только на архитектуре Flux.中心化,信号控制механизм, можно резюмировать очень простую реализацию:

export default class Store {
  constructor(options) {
    this._state = options.state;
    this._mutations = options.mutations;
  }

  get state() {
    return this._state;
  }

  commit(type, payload) {
    this._mutations[type].apply(this, [this.state].concat([...payload]));
  }
}

Это основа понимания Vuex, во всем коде всего две логики:

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

С этим кодом много проблем, например:

  • Используйте простые объекты в качестве состояния
  • Мутация состояния достигается только путем изменения значения свойства объекта состояния.
  • Не существует эффективного механизма для предотвращения ошибочного изменения объекта состояния.

Эти проблемы дизайна также существуют в Vuex, который похож наVue.$watchМеханизм очень тесно связан (см. Ниже), что я лично нахожу крайне слабым.

сигнальный механизм

Vuex предоставляет два интерфейса, связанных с сигналами, и его исходный код можно сократить как:

export default class Store {
  ...
  commit (_type, _payload, _options) {
    ...
    const entry = this._mutations[type]
    this._withCommit(() => {
      entry.forEach(function commitIterator (handler) {
        handler(payload)
      })
    })
    this._subscribers.forEach(sub => sub(mutation, this.state))
    ...
  }

  dispatch (_type, _payload) {
    ...
    const entry = this._actions[type]
    return entry.length > 1
      ? Promise.all(entry.map(handler => handler(payload)))
      : entry[0](payload)
  }
  ...
}

Разница между ними заключается в следующем:

  1. dispatchсрабатываетactionПерезвоните;commitзапущенныйmutationПерезвоните.
  2. dispatchвернуть обещание;commitНет возвращаемого значения.

Этот замысел дизайна в основном касается разделения обязанностей, а единица действия используется для описаниячто случилось;mutationИспользуется для изменения состояния уровня данныхstate. Vuex использует похожие интерфейсы, чтобы разместить их в одном и том же месте, но на самом деле у этого уровня дизайна интерфейса есть недостатки:

  1. Для действия и мутации требуется система типов.
  2. Разрешить прикладному уровню напрямую обходить действиеcommit mutation
  3. состояние неimmutable, и разрешить изменение в действииstate

Хотя это и повышает удобство, но может привести к следующим антипаттернам для начинающих:

  • Разработаны два набора систем типов, которые не могут быть ортогональными.
  • Создает иллюзию «передачи мутации напрямую» и разрушает сигнальный механизм Flux.
  • Состояние случайно изменяется в действии без дружественного механизма отслеживания (особенно это серьезно в геттере).

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

Однонаправленный поток данных

Поток данных здесь относится к состоянию от Vuex до компонента Vue.props/computed/dataОтображение блока состояния, то есть как получить состояние в компоненте. Vuex официально рекомендуется использоватьmapGetter,mapStateИнтерфейс реализует привязку данных.

mapState

Функция очень проста, а логику кода можно представить так:

export const mapState = normalizeNamespace((namespace, states) => {
  const res = {}
  ...
  normalizeMap(states).forEach(({ key, val }) => {
    res[key] = function mappedState() {
      ...
      return typeof val === 'function' ?
        val.call(this, state, getters) :
        state[val]
    }
  })
  ...
  return res
})

mapState напрямую считывает свойства объекта состояния. Стоит отметить, чтоres[key]Как правило, она монтируется как функция во внешнем объекте.thisУказывает на смонтированный компонент Vue.

mapGetter

Функция также очень проста, и логика ее кода такова:

export const mapGetters = normalizeNamespace((namespace, getters) => {
  const res = {}
  normalizeMap(getters).forEach(({ key, val }) => {

    res[key] = function mappedGetter() {
        ...
        return this.$store.getters[val]
      }
      ...
  })
  return res
})

mapGetter обращается к монтированию компонента$storeСвойство getters экземпляра.

из состояния в геттер

Свойство getter в Vuex во всех аспектах очень похоже на свойство вычисляемого в Vue.На самом деле, свойство получения реализовано на основе вычисленного. Его основная логика включает в себя:

function resetStoreVM(store, state, hot) {
  ...
  store.getters = {}
  const wrappedGetters = store._wrappedGetters
  const computed = {}
    // 遍历 getter 配置,生成 computed 属性
  forEachValue(wrappedGetters, (fn, key) => {
    computed[key] = () => fn(store)
    Object.defineProperty(store.getters, key, {
      // 获取 vue 实例属性
      get: () => store._vm[key],
      enumerable: true // for local getters
    })
  })

  // 新建 Vue 实例,专门用于监听属性变更
  store._vm = new Vue({
      data: {
        ?state: state
      },
      computed
    })
    ...
}

Как видно из кода, Vuex размещает весь объект состояния в свойстве данных экземпляра vue в обмен на все состояние Vue.watchмеханизм. А свойство getter реализовано путем возврата вычисляемого свойства экземпляра, что не является тонким способом сделать это. Вопрос в том:

  1. Vuex тесно связан с Vue, что делает невозможным переход на другие среды.
  2. ВьюwatchМеханизм реализован на основе функций чтения и записи атрибутов.Если корневой узел будет заменен напрямую, это приведет к сбою различных обратных вызовов субатрибутов, то есть невозможно будет достичьimmutableхарактеристика

послесловие

Самое большое ощущение, которое вызывает у меня Vuex: удобство, одна и та же функция имеет множество логических единиц с разной семантикой, и разделение обязанностей очень хорошее.Если строго следовать спецификации, можно действительно очень хорошо организовать код; интерфейс тоже очень простой и удобный.Да, очень дружелюбный к разработчикам. С точки зрения количества пользователей, влияния и т. д., это, несомненно, очень хороший фреймворк. Конечно, некоторые из высказанных здесь мнений также имеют разные мнения, и цель их не более чем провокация других.