Как мы все знаем,VuexдаFluxРеализация архитектуры. Flux четко устанавливает различные функциональные блоки в сценарии управления данными, и его основные принципы таковы:
- Централизованное государственное управление
- Статус может быть только специальным
突变единица измерения - Прикладной уровень инициирует изменения, отправляя сигналы (обычно называемые действиями).
Vuex также разработан с учетом этих рекомендаций.storeКласс обеспечивает основные функции шаблона Flux. В дополнение к удовлетворению основных требований архитектуры дополнительно разработаны многие удобные меры:
- Изолируйте блоки данных с помощью «модульной» конструкции
- Обеспечьте механизм получения для улучшения возможности повторного использования кода.
- использовать
Vue.$watchметод реализации потока данных - Нулевая конфигурация, естественно интегрированная в среду 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, во всем коде всего две логики:
- пройти через
_stateСвойства для достижения уровня централизованного автономного центра обработки данных. - пройти через
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)
}
...
}
Разница между ними заключается в следующем:
-
dispatchсрабатываетactionПерезвоните;commitзапущенныйmutationПерезвоните. -
dispatchвернуть обещание;commitНет возвращаемого значения.
Этот замысел дизайна в основном касается разделения обязанностей, а единица действия используется для описаниячто случилось;mutationИспользуется для изменения состояния уровня данныхstate. Vuex использует похожие интерфейсы, чтобы разместить их в одном и том же месте, но на самом деле у этого уровня дизайна интерфейса есть недостатки:
- Для действия и мутации требуется система типов.
- Разрешить прикладному уровню напрямую обходить действие
commitmutation - состояние не
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 реализовано путем возврата вычисляемого свойства экземпляра, что не является тонким способом сделать это. Вопрос в том:
- Vuex тесно связан с Vue, что делает невозможным переход на другие среды.
- Вью
watchМеханизм реализован на основе функций чтения и записи атрибутов.Если корневой узел будет заменен напрямую, это приведет к сбою различных обратных вызовов субатрибутов, то есть невозможно будет достичьimmutableхарактеристика
послесловие
Самое большое ощущение, которое вызывает у меня Vuex: удобство, одна и та же функция имеет множество логических единиц с разной семантикой, и разделение обязанностей очень хорошее.Если строго следовать спецификации, можно действительно очень хорошо организовать код; интерфейс тоже очень простой и удобный.Да, очень дружелюбный к разработчикам. С точки зрения количества пользователей, влияния и т. д., это, несомненно, очень хороший фреймворк. Конечно, некоторые из высказанных здесь мнений также имеют разные мнения, и цель их не более чем провокация других.