предисловие
После прочтения модуля я чувствую, что у меня естьnamespaced
Модуль — это настоящий модуль, поэтому я добавил эту статью.
Пронередность имен делает Getter, мутации и действия по-настоящему модульными, что позволяет хранить мутации, специфичные модуля и т. Д. В этой статье давайте посмотрим, как реализован намен?
Подготовить
На этот раз я читаю vuex2.1.0
Версия, ткните пожалуйста в исходникздесь. Рекомендуется скачать и открыть код, чтобы следовать расшифровке, иначе вы можете выглядеть растерянным.
интерпретировать
Код Vuex 2.1.0 примерно похож на 2.0.0, за исключением того, что модуль извлекается в каталог. В нем есть два файла, Module-Collection.js и module.js, и интерпретация этих двух файлов будет включена в следующую.
Или начните с конструктора хранилища и начните искать код, связанный с модулем и пространством имен.
constructor (options = {}) {
this._modules = new ModuleCollection(options)
this._modulesNamespaceMap = Object.create(null)
// init root module.
// this also recursively registers all sub-modules
// and collects all module getters inside this._wrappedGetters
installModule(this, state, [], this._modules.root)
}
Давайте положим_modulesNamespaceMap
Вынесенное отдельно, это свойство в основном используется вспомогательной функцией mapState и используется при интерпретации mapState.
Таким образом, конструктор делает с модулем две вещи: первая — инициализировать _modules, передавая опции параметра конфигурации, а вторая — регистрировать модуль с помощью installModule.
Дел не так много, но кодов в ней реализовано довольно много, морально все должны быть готовы ха-ха.
ModuleCollection
Во-первых, давайте посмотрим, что делает new ModuleCollection для инициализации _modules. Таргетингmodule-collection.js
File, увидел его конструктор:
constructor (rawRootModule) {
// register root module (Vuex.Store options)
this.root = new Module(rawRootModule, false)
// register all nested modules
if (rawRootModule.modules) {
forEachValue(rawRootModule.modules, (rawModule, key) => {
this.register([key], rawModule, false)
})
}
}
构造函数做了也是两件事情,一件是注册了一个根 module,另一个是遍历注册子 module。 Открытьmodule.js
Взгляните на основной используемый код:
export default class Module {
constructor (rawModule, runtime) {
this.runtime = runtime
this._children = Object.create(null)
this._rawModule = rawModule
}
addChild (key, module) {
this._children[key] = module
}
getChild (key) {
return this._children[key]
}
}
Добавьте в конструктор _children, то есть подмодуль, а затем сохраните текущий модуль в _rawModule. Затем будут использоваться два метода addChild и getChild, Как следует из названия, они используются для добавления подмодулей и получения подмодулей.
Вернитесь ко второму шагу конструктора ModuleCollection и найдитеregister
метод:
// 得到对应 path 的 module
get (path) {
return path.reduce((module, key) => {
return module.getChild(key)
}, this.root)
}
register (path, rawModule, runtime = true) {
// path.slice(0, -1) 表示去掉最后一个
// 取得父 module
const parent = this.get(path.slice(0, -1))
// new 一个新的 module
const newModule = new Module(rawModule, runtime)
// 添加子 module
parent.addChild(path[path.length - 1], newModule)
// register nested modules
if (rawModule.modules) {
// 递归注册子 module
forEachValue(rawModule.modules, (rawChildModule, key) => {
this.register(path.concat(key), rawChildModule, runtime)
})
}
}
Были добавлены комментарии к коду, и вы можете видеть, что регистр чем-то похож на состояние рекурсивного набора при интерпретации модуля ранее. После рекурсии здесь будет сгенерирован экземпляр модуля, если у экземпляра есть дочерние модули, он будет сохранен в его атрибуте _children и так далее.
installModule
После инициализации свойства магазина _Modules следующий шаг - модуль регистрации. Методы нацеливания MODELModule:
function installModule (store, rootState, path, module, hot) {
const isRoot = !path.length
const namespace = store._modules.getNamespace(path)
// register in namespace map
if (namespace) {
store._modulesNamespaceMap[namespace] = module
}
// set state
if (!isRoot && !hot) {
const parentState = getNestedState(rootState, path.slice(0, -1))
const moduleName = path[path.length - 1]
store._withCommit(() => {
Vue.set(parentState, moduleName, module.state)
})
}
const local = module.context = makeLocalContext(store, namespace)
module.forEachMutation((mutation, key) => {
const namespacedType = namespace + key
registerMutation(store, namespacedType, mutation, path)
})
module.forEachAction((action, key) => {
const namespacedType = namespace + key
registerAction(store, namespacedType, action, local, path)
})
module.forEachGetter((getter, key) => {
const namespacedType = namespace + key
registerGetter(store, namespacedType, getter, local, path)
})
module.forEachChild((child, key) => {
installModule(store, rootState, path.concat(key), child, hot)
})
}
getNamespace
getNamespace (path) {
let module = this.root
return path.reduce((namespace, key) => {
module = module.getChild(key)
return namespace + (module.namespaced ? key + '/' : '')
}, '')
}
// options
{
modules: {
a: {
modules: {
b: {
// ...
}
}
}
}
}
Пространства имен, соответствующие массиву путей:
// []
/
// [a]
/a/
// [a, b]
/a/b/
registerAction
После получения пространства имен следующим шагом будет регистрация действия.
const local = module.context = makeLocalContext(store, namespace)
module.forEachAction((action, key) => {
const namespacedType = namespace + key
registerAction(store, namespacedType, action, local, path)
})
Он был интерпретирован до второго шага registerAction, не более чем подмодуль в действии как ключ, представленный namespacedType, используемый для различения хранилища ключей._actions. Так что первый шаг в интерпретации этого кода в основном makeLocalContext.
function makeLocalContext (store, namespace) {
const noNamespace = namespace === ''
const local = {
dispatch: noNamespace ? store.dispatch : (_type, _payload, _options) => {
const args = unifyObjectStyle(_type, _payload, _options)
const { payload, options } = args
let { type } = args
if (!options || !options.root) {
type = namespace + type
if (!store._actions[type]) {
console.error(`[vuex] unknown local action type: ${args.type}, global type: ${type}`)
return
}
}
return store.dispatch(type, payload)
}
}
return local
}
После упрощения кода осталось еще много. Метод unifyObjectStyle предназначен только для поддержки нескольких форматов передачи параметров и не будет здесь подробно объясняться.
В основном это делается для упрощения пути первого параметра при отправке действия после параметров, возвращаемых действием. Это может быть легче понять, взглянув на следующий код:
modules: {
foo: {
namespaced: true,
actions: {
// 在这个模块中, dispatch 和 commit 也被局部化了
// 他们可以接受 `root` 属性以访问根 dispatch 或 commit
someAction ({ dispatch, commit, getters, rootGetters }) {
dispatch('someOtherAction') // -> 'foo/someOtherAction'
dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction'
}
}
}
}
helper
Вспомогательные функции vuex имеют функции привязки с пространствами имен, как показано ниже:
computed: {
...mapState('some/nested/module', {
a: state => state.a,
b: state => state.b
})
},
methods: {
...mapActions('some/nested/module', [
'foo',
'bar'
])
}
С точки зрения mapActions, он в основном оборачивает слой normalizeNamespace в исходную реализацию. Откройте файл helper.js и найдите метод normalizeNamespace:
function normalizeNamespace (fn) {
return (namespace, map) => {
if (typeof namespace !== 'string') {
map = namespace
namespace = ''
} else if (namespace.charAt(namespace.length - 1) !== '/') {
namespace += '/'
}
return fn(namespace, map)
}
}
Главное, соединить путь к вызову store.__actions.
Но состояние не хранится в массиве, подобно действию, поэтому вам нужно использовать _modulesNamespaceMap, чтобы получить текущий модуль, чтобы получить состояние внутри.
Конкретного кода реализации _modulesNamespaceMap не так много, поэтому я не буду его подробно интерпретировать.
Суммировать
Я, наконец, закончил интерпретацию пространства имен, которая сложнее, чем предыдущая интерпретация, и количество задействованного кода намного больше, поэтому некоторые коды не удалось подробно интерпретировать.
Модуль добавляет пространство имен, извлекает весь модуль и рекурсивно инициализирует _modules для облегчения поиска и использования последующих модулей.
Пространство имен используется как путь и как ключ массива для доступа к действиям подмодуля и т.д. Таким образом, действия в подмодулях могут быть доступны отдельно. По сравнению с предыдущей версией 2.0.0, которая могла обращаться только к состоянию в подмодуле, но не к действию и т. д., пространство имен в версии 2.1.0 было добавлено, чтобы сделать его более модульным.