在构造好这个 store 后,需要提供一些 API 对这个 store 做存取的操作

数据获取

Vuex 最终存储的数据是在 state 上的,之前分析过在 store.state 存储的是 root state,那么对于模块上的 state,假设有 2 个嵌套的 modules,它们的 key 分别为 a 和 b,可以通过 store.state.a.b.xxx 的方式去获取
它的实现是在发生在 installModule 的时候

  1. function installModule (store, rootState, path, module, hot) {
  2. const isRoot = !path.length
  3. // ...
  4. // set state
  5. if (!isRoot && !hot) {
  6. const parentState = getNestedState(rootState, path.slice(0, -1))
  7. const moduleName = path[path.length - 1]
  8. store._withCommit(() => {
  9. if (__DEV__) {
  10. if (moduleName in parentState) {
  11. console.warn(
  12. `[vuex] state field "${moduleName}" was overridden by a module with the same name at "${path.join('.')}"`
  13. )
  14. }
  15. }
  16. Vue.set(parentState, moduleName, module.state)
  17. })
  18. }
  19. // ...
  20. }

在递归执行 installModule 的过程中,就完成了整个 state 的建设,这样就可以通过 module 名的 path 去访问到一个深层 module 的 state
有些时候,获取的数据不仅仅是一个 state,而是由多个 state 计算而来,Vuex 提供了 getters,允许定义一个 getter 函数,如下

  1. getters: {
  2. total (state, getters, localState, localGetters) {
  3. // 可访问全局state和getters,以及如果是在modules下面,可以访问到局部state和局部getters
  4. return state.a + state.b
  5. }
  6. }

在 installModule 的过程中,递归执行了所有 getters 定义的注册,在之后的 resetStoreVM 过程中,执行了 store.getters 的初始化工作

  1. function installModule (store, rootState, path, module, hot) {
  2. // ...
  3. const namespace = store._modules.getNamespace(path)
  4. // ...
  5. const local = module.context = makeLocalContext(store, namespace, path)
  6. //...
  7. module.forEachGetter((getter, key) => {
  8. const namespacedType = namespace + key
  9. registerGetter(store, namespacedType, getter, local)
  10. })
  11. // ...
  12. }
  13. function registerGetter (store, type, rawGetter, local) {
  14. if (store._wrappedGetters[type]) {
  15. if (__DEV__) {
  16. console.error(`[vuex] duplicate getter key: ${type}`)
  17. }
  18. return
  19. }
  20. store._wrappedGetters[type] = function wrappedGetter (store) {
  21. return rawGetter(
  22. local.state, // local state
  23. local.getters, // local getters
  24. store.state, // root state
  25. store.getters // root getters
  26. )
  27. }
  28. }
  29. function resetStoreVM (store, state, hot) {
  30. // ...
  31. // bind store public getters
  32. store.getters = {}
  33. // reset local getters cache
  34. store._makeLocalGettersCache = Object.create(null)
  35. const wrappedGetters = store._wrappedGetters
  36. const computed = {}
  37. forEachValue(wrappedGetters, (fn, key) => {
  38. // use computed to leverage its lazy-caching mechanism
  39. // direct inline function use will lead to closure preserving oldVm.
  40. // using partial to return function with only arguments preserved in closure environment.
  41. computed[key] = partial(fn, store)
  42. Object.defineProperty(store.getters, key, {
  43. get: () => store._vm[key],
  44. enumerable: true // for local getters
  45. })
  46. })
  47. // use a Vue instance to store the state tree
  48. // suppress warnings just in case the user has added
  49. // some funky global mixins
  50. const silent = Vue.config.silent
  51. Vue.config.silent = true
  52. store._vm = new Vue({
  53. data: {
  54. $$state: state
  55. },
  56. computed
  57. })
  58. // ...
  59. }

在 installModule 的过程中,为建立了每个模块的上下文环境, 因此当访问 store.getters.xxx 的时候,实际上就是执行了 rawGetter(local.state,…),rawGetter 就是定义的 getter 方法,这也就是为什么getter 函数支持这四个参数,并且除了全局的 state 和 getter 外,还可以访问到当前 module 下的 state 和 getter

数据存储

Vuex 对数据存储的存储本质上就是对 state 做修改,并且只允许通过提交 mutaion 的形式去修改 state,mutation 是一个函数,如下

  1. mutations: {
  2. increment (state) {
  3. state.count++
  4. }
  5. }

mutations 的初始化也是在 installModule 的时候

  1. function installModule (store, rootState, path, module, hot) {
  2. // ...
  3. const namespace = store._modules.getNamespace(path)
  4. // ...
  5. const local = module.context = makeLocalContext(store, namespace, path)
  6. module.forEachMutation((mutation, key) => {
  7. const namespacedType = namespace + key
  8. registerMutation(store, namespacedType, mutation, local)
  9. })
  10. // ...
  11. }
  12. function registerMutation (store, type, handler, local) {
  13. const entry = store._mutations[type] || (store._mutations[type] = [])
  14. entry.push(function wrappedMutationHandler (payload) {
  15. handler.call(store, local.state, payload)
  16. })
  17. }

store 提供了commit 方法让提交一个 mutation

  1. // _type就是mutation的type
  2. commit (_type, _payload, _options) {
  3. // check object-style commit
  4. const {
  5. type,
  6. payload,
  7. options
  8. } = unifyObjectStyle(_type, _payload, _options)
  9. const mutation = { type, payload }
  10. const entry = this._mutations[type]
  11. if (!entry) {
  12. if (__DEV__) {
  13. console.error(`[vuex] unknown mutation type: ${type}`)
  14. }
  15. return
  16. }
  17. this._withCommit(() => {
  18. entry.forEach(function commitIterator (handler) {
  19. handler(payload)
  20. })
  21. })
  22. this._subscribers
  23. .slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe
  24. .forEach(sub => sub(mutation, this.state))
  25. if (
  26. __DEV__ &&
  27. options && options.silent
  28. ) {
  29. console.warn(
  30. `[vuex] mutation type: ${type}. Silent option has been removed. ` +
  31. 'Use the filter functionality in the vue-devtools'
  32. )
  33. }
  34. }

可以从 store._mutations 找到对应的函数数组,遍历它们执行获取到每个 handler 然后执行,实际上就是执行了 wrappedMutationHandler(playload),接着会执行定义的 mutation 函数,并传入当前模块的 state,所以mutation 函数也就是对当前模块的 state 做修改
需要注意的是, mutation 必须是同步函数,但是在开发实际项目中,经常会遇到要先去发送一个请求,然后根据请求的结果去修改 state,那么单纯只通过 mutation 是无法完成需求,因此 Vuex 又给设计了一个 action 的概念
action 类似于 mutation,不同在于 action 提交的是 mutation,而不是直接操作 state,并且它可以包含任意异步操作。例如

  1. mutations: {
  2. increment (state) {
  3. state.count++
  4. }
  5. },
  6. actions: {
  7. increment (context) {
  8. setTimeout(() => {
  9. context.commit('increment')
  10. }, 0)
  11. }
  12. }

actions 的初始化也是在 installModule 的时候

  1. function installModule (store, rootState, path, module, hot) {
  2. // ...
  3. const namespace = store._modules.getNamespace(path)
  4. // ...
  5. const local = module.context = makeLocalContext(store, namespace, path)
  6. // ...
  7. module.forEachAction((action, key) => {
  8. const type = action.root ? key : namespace + key
  9. const handler = action.handler || action
  10. registerAction(store, type, handler, local)
  11. })
  12. // ...
  13. }
  14. function registerAction (store, type, handler, local) {
  15. const entry = store._actions[type] || (store._actions[type] = [])
  16. entry.push(function wrappedActionHandler (payload) {
  17. let res = handler.call(store, {
  18. dispatch: local.dispatch,
  19. commit: local.commit,
  20. getters: local.getters,
  21. state: local.state,
  22. rootGetters: store.getters,
  23. rootState: store.state
  24. }, payload)
  25. if (!isPromise(res)) {
  26. res = Promise.resolve(res)
  27. }
  28. if (store._devtoolHook) {
  29. return res.catch(err => {
  30. store._devtoolHook.emit('vuex:error', err)
  31. throw err
  32. })
  33. } else {
  34. return res
  35. }
  36. })
  37. }

store 提供了dispatch 方法让提交一个 action

  1. // _type就是action的type
  2. dispatch (_type, _payload) {
  3. // check object-style dispatch
  4. const {
  5. type,
  6. payload
  7. } = unifyObjectStyle(_type, _payload)
  8. const action = { type, payload }
  9. const entry = this._actions[type]
  10. if (!entry) {
  11. if (__DEV__) {
  12. console.error(`[vuex] unknown action type: ${type}`)
  13. }
  14. return
  15. }
  16. try {
  17. this._actionSubscribers
  18. .slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe
  19. .filter(sub => sub.before)
  20. .forEach(sub => sub.before(action, this.state))
  21. } catch (e) {
  22. if (__DEV__) {
  23. console.warn(`[vuex] error in before action subscribers: `)
  24. console.error(e)
  25. }
  26. }
  27. const result = entry.length > 1
  28. ? Promise.all(entry.map(handler => handler(payload)))
  29. : entry[0](payload)
  30. return new Promise((resolve, reject) => {
  31. result.then(res => {
  32. try {
  33. this._actionSubscribers
  34. .filter(sub => sub.after)
  35. .forEach(sub => sub.after(action, this.state))
  36. } catch (e) {
  37. if (__DEV__) {
  38. console.warn(`[vuex] error in after action subscribers: `)
  39. console.error(e)
  40. }
  41. }
  42. resolve(res)
  43. }, error => {
  44. try {
  45. this._actionSubscribers
  46. .filter(sub => sub.error)
  47. .forEach(sub => sub.error(action, this.state, error))
  48. } catch (e) {
  49. if (__DEV__) {
  50. console.warn(`[vuex] error in error action subscribers: `)
  51. console.error(e)
  52. }
  53. }
  54. reject(error)
  55. })
  56. })
  57. }

可以从 store._actions 找到对应的函数数组,遍历它们执行获取到每个 handler 然后执行,实际上就是执行了 wrappedActionHandler(payload),接着会执行定义的 action 函数,并传入一个对象,包含了当前模块下的 dispatch、commit、getters、state,以及全局的 rootState 和 rootGetters,所以定义的 action 函数能拿到当前模块下的 commit 方法
因此 action 比自己写一个函数执行异步操作然后提交 muataion 的好处是在于它可以在参数中获取到当前模块的一些方法和状态,Vuex 已经做好了这些

语法糖

store 是 Store 对象的一个实例,它是一个原生的 Javascript 对象,可以在任意地方使用它们
但大部分的使用场景还是在组件中使用,那么之前介绍过,在 Vuex 安装阶段,它会往每一个组件实例上混入 beforeCreate 钩子函数,然后往组件实例上添加一个 $store 的实例,它指向的就是实例化的 store,因此可以在组件中访问到 store 的任何属性和方法
比如在组件中访问 state

  1. const Counter = {
  2. template: `<div>{{ count }}</div>`,
  3. computed: {
  4. count () {
  5. return this.$store.state.count
  6. }
  7. }
  8. }

但是当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。同样这些问题也在存于 getter、mutation 和 action
为了解决这个问题,Vuex 提供了一系列mapXXX辅助函数帮助实现在组件中可以很方便的注入store的属性和方法

mapState

mapState的用法

  1. // 在单独构建的版本中辅助函数为 Vuex.mapState
  2. import { mapState } from 'vuex'
  3. export default {
  4. // ...
  5. computed: mapState({
  6. // 箭头函数可使代码更简练
  7. count: state => state.count,
  8. // 传字符串参数 'count' 等同于 `state => state.count`
  9. countAlias: 'count',
  10. // 为了能够使用 `this` 获取局部状态,必须使用常规函数
  11. countPlusLocalState (state) {
  12. return state.count + this.localCount
  13. }
  14. })
  15. }

mapState 方法定义在 src/helpers.js 中

  1. /**
  2. * Reduce the code which written in Vue.js for getting the state.
  3. * @param {String} [namespace] - Module's namespace
  4. * @param {Object|Array} states # Object's item can be a function which accept state and getters for param, you can do something for state and getters in it.
  5. * @param {Object}
  6. */
  7. // mapState 通过执行normalizeNamespace返回的函数
  8. // namespace表示命名空间 map表示具体的对象 namespace可不传
  9. export const mapState = normalizeNamespace((namespace, states) => {
  10. const res = {}
  11. if (__DEV__ && !isValidMap(states)) {
  12. console.error('[vuex] mapState: mapper parameter must be either an Array or an Object')
  13. }
  14. normalizeMap(states).forEach(({ key, val }) => {
  15. res[key] = function mappedState () {
  16. let state = this.$store.state
  17. let getters = this.$store.getters
  18. if (namespace) {
  19. const module = getModuleByNamespace(this.$store, 'mapState', namespace)
  20. if (!module) {
  21. return
  22. }
  23. state = module.context.state
  24. getters = module.context.getters
  25. }
  26. return typeof val === 'function'
  27. ? val.call(this, state, getters)
  28. : state[val]
  29. }
  30. // mark vuex getter for devtools
  31. res[key].vuex = true
  32. })
  33. return res
  34. })
  35. /**
  36. * Return a function expect two param contains namespace and map. it will normalize the namespace and then the param's function will handle the new namespace and the map.
  37. * @param {Function} fn
  38. * @return {Function}
  39. */
  40. function normalizeNamespace (fn) {
  41. return (namespace, map) => {
  42. if (typeof namespace !== 'string') {
  43. map = namespace
  44. namespace = ''
  45. } else if (namespace.charAt(namespace.length - 1) !== '/') {
  46. namespace += '/'
  47. }
  48. return fn(namespace, map)
  49. }
  50. }
  51. /**
  52. * Normalize the map
  53. * normalizeMap([1, 2, 3]) => [ { key: 1, val: 1 }, { key: 2, val: 2 }, { key: 3, val: 3 } ]
  54. * normalizeMap({a: 1, b: 2, c: 3}) => [ { key: 'a', val: 1 }, { key: 'b', val: 2 }, { key: 'c', val: 3 } ]
  55. * @param {Array|Object} map
  56. * @return {Array}
  57. */
  58. function normalizeMap (map) {
  59. if (!isValidMap(map)) {
  60. return []
  61. }
  62. return Array.isArray(map)
  63. ? map.map(key => ({ key, val: key }))
  64. : Object.keys(map).map(key => ({ key, val: map[key] }))
  65. }

当执行 mapState(map) 函数的时候,实际上就是执行 normalizeNamespace 包裹的函数,然后把 map 作为参数 states 传入
mapState 最终是要构造一个对象,每个对象的元素都是一个方法,因为这个对象是要扩展到组件的 computed 计算属性中的
函数首先执行 normalizeMap 方法,把这个 states 变成一个数组,数组的每个元素都是 {key, val} 的形式;接着再遍历这个数组,以 key 作为对象的 key,值为一个 mappedState 的函数,在这个函数的内部,获取到 $store.getters 和 $store.state,然后再判断数组的 val 如果是一个函数,执行该函数,传入 state 和 getters,否则直接访问 state[val]
比起一个个手动声明计算属性,mapState 确实要方便许多
下面来看一下 namespace 的作用
当想访问一个子模块的 state 的时候,可能需要这样访问

  1. computed: {
  2. mapState({
  3. a: state => state.some.nested.module.a,
  4. b: state => state.some.nested.module.b
  5. })
  6. },

这样从写法上就很不友好,mapState 支持传入 namespace, 因此可以这么写

  1. computed: {
  2. mapState('some/nested/module', {
  3. a: state => state.a,
  4. b: state => state.b
  5. })
  6. },

这样看起来就清爽许多
在 mapState 的实现中,如果有 namespace,则尝试去通过 getModuleByNamespace(this.$store, ‘mapState’, namespace) 对应的 module,然后把 state 和 getters 修改为 module 对应的 state 和 getters

  1. /**
  2. * Search a special module from store by namespace. if module not exist, print error message.
  3. * @param {Object} store
  4. * @param {String} helper
  5. * @param {String} namespace
  6. * @return {Object}
  7. */
  8. function getModuleByNamespace (store, helper, namespace) {
  9. const module = store._modulesNamespaceMap[namespace]
  10. if (__DEV__ && !module) {
  11. console.error(`[vuex] module namespace not found in ${helper}(): ${namespace}`)
  12. }
  13. return module
  14. }

在 Vuex 初始化执行 installModule 的过程中,初始化了这个映射表

  1. function installModule (store, rootState, path, module, hot) {
  2. // ...
  3. const namespace = store._modules.getNamespace(path)
  4. // register in namespace map
  5. if (module.namespaced) {
  6. if (store._modulesNamespaceMap[namespace] && __DEV__) {
  7. console.error(`[vuex] duplicate namespace ${namespace} for the namespaced module ${path.join('/')}`)
  8. }
  9. store._modulesNamespaceMap[namespace] = module
  10. }
  11. // ...
  12. }

mapGetters

mapGetters的用法

  1. import { mapGetters } from 'vuex'
  2. export default {
  3. // ...
  4. computed: {
  5. // 使用对象展开运算符将 getter 混入 computed 对象中
  6. mapGetters([
  7. 'doneTodosCount',
  8. 'anotherGetter',
  9. // ...
  10. ])
  11. }
  12. }

和 mapState 类似,mapGetters 是将 store 中的 getter 映射到局部计算属性,来看一下它的定义

  1. /**
  2. * Reduce the code which written in Vue.js for getting the getters
  3. * @param {String} [namespace] - Module's namespace
  4. * @param {Object|Array} getters
  5. * @return {Object}
  6. */
  7. export const mapGetters = normalizeNamespace((namespace, getters) => {
  8. const res = {}
  9. if (__DEV__ && !isValidMap(getters)) {
  10. console.error('[vuex] mapGetters: mapper parameter must be either an Array or an Object')
  11. }
  12. normalizeMap(getters).forEach(({ key, val }) => {
  13. // The namespace has been mutated by normalizeNamespace
  14. val = namespace + val
  15. res[key] = function mappedGetter () {
  16. if (namespace && !getModuleByNamespace(this.$store, 'mapGetters', namespace)) {
  17. return
  18. }
  19. if (__DEV__ && !(val in this.$store.getters)) {
  20. console.error(`[vuex] unknown getter: ${val}`)
  21. return
  22. }
  23. return this.$store.getters[val]
  24. }
  25. // mark vuex getter for devtools
  26. res[key].vuex = true
  27. })
  28. return res
  29. })

mapGetters 也同样支持 namespace,如果不写 namespace ,访问一个子 module 的属性需要写很长的 key,一旦使用了 namespace,就可以方便我们的书写,每个 mappedGetter 的实现实际上就是取 this.$store.getters[val]

mapMutations

可以在组件中使用 this.$store.commit(‘xxx’) 提交 mutation,或者使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 的调用
mapMutations的用法

  1. import { mapMutations } from 'vuex'
  2. export default {
  3. // ...
  4. methods: {
  5. ...mapMutations([
  6. 'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
  7. // `mapMutations` 也支持载荷:
  8. 'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
  9. ]),
  10. ...mapMutations({
  11. add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
  12. })
  13. }
  14. }

mapMutations 支持传入一个数组或者一个对象,目标都是组件中对应的 methods 映射为 store.commit 的调用
来看一下它的定义

  1. /**
  2. * Reduce the code which written in Vue.js for committing the mutation
  3. * @param {String} [namespace] - Module's namespace
  4. * @param {Object|Array} mutations # Object's item can be a function which accept `commit` function as the first param, it can accept another params. You can commit mutation and do any other things in this function. specially, You need to pass anthor params from the mapped function.
  5. * @return {Object}
  6. */
  7. export const mapMutations = normalizeNamespace((namespace, mutations) => {
  8. const res = {}
  9. if (__DEV__ && !isValidMap(mutations)) {
  10. console.error('[vuex] mapMutations: mapper parameter must be either an Array or an Object')
  11. }
  12. normalizeMap(mutations).forEach(({ key, val }) => {
  13. res[key] = function mappedMutation (...args) {
  14. // Get the commit method from store
  15. let commit = this.$store.commit
  16. if (namespace) {
  17. const module = getModuleByNamespace(this.$store, 'mapMutations', namespace)
  18. if (!module) {
  19. return
  20. }
  21. commit = module.context.commit
  22. }
  23. return typeof val === 'function'
  24. ? val.apply(this, [commit].concat(args))
  25. : commit.apply(this.$store, [val].concat(args))
  26. }
  27. })
  28. return res
  29. })

可以看到 mappedMutation 同样支持了 namespace,并且支持了传入额外的参数 args,作为提交 mutation 的 payload,最终就是执行了 store.commit 方法,并且这个 commit 会根据传入的 namespace 映射到对应 module 的 commit 上

mapActions

在组件中使用 this.$store.dispatch(‘xxx’) 提交 action,或者使用 mapActions 辅助函数将组件中的 methods 映射为 store.dispatch 的调用
mapActions 在用法上和 mapMutations 几乎一样,实现也很类似

  1. /**
  2. * Reduce the code which written in Vue.js for dispatch the action
  3. * @param {String} [namespace] - Module's namespace
  4. * @param {Object|Array} actions # Object's item can be a function which accept `dispatch` function as the first param, it can accept anthor params. You can dispatch action and do any other things in this function. specially, You need to pass anthor params from the mapped function.
  5. * @return {Object}
  6. */
  7. export const mapActions = normalizeNamespace((namespace, actions) => {
  8. const res = {}
  9. if (__DEV__ && !isValidMap(actions)) {
  10. console.error('[vuex] mapActions: mapper parameter must be either an Array or an Object')
  11. }
  12. normalizeMap(actions).forEach(({ key, val }) => {
  13. res[key] = function mappedAction (...args) {
  14. // get dispatch function from store
  15. let dispatch = this.$store.dispatch
  16. if (namespace) {
  17. const module = getModuleByNamespace(this.$store, 'mapActions', namespace)
  18. if (!module) {
  19. return
  20. }
  21. dispatch = module.context.dispatch
  22. }
  23. return typeof val === 'function'
  24. ? val.apply(this, [dispatch].concat(args))
  25. : dispatch.apply(this.$store, [val].concat(args))
  26. }
  27. })
  28. return res
  29. })

和 mapMutations 的实现几乎一样,不同的是把 commit 方法换成了 dispatch

动态更新模块

在 Vuex 初始化阶段构造了模块树,初始化了模块上各个部分
在有一些场景下,需要动态去注入一些新的模块,Vuex 提供了模块动态注册功能,在 store 上提供了一个 registerModule 的 API

  1. // path模块路径 rawModule模块定义
  2. registerModule (path, rawModule, options = {}) {
  3. if (typeof path === 'string') path = [path]
  4. if (__DEV__) {
  5. assert(Array.isArray(path), `module path must be a string or an Array.`)
  6. assert(path.length > 0, 'cannot register the root module by using registerModule.')
  7. }
  8. // 执行register方法扩展模块树
  9. this._modules.register(path, rawModule)
  10. // 安装模块
  11. installModule(this, this.state, path, this._modules.get(path), options.preserveState)
  12. // reset store to update getters...
  13. // 重新实例化store.vm,并销毁旧的store.vm
  14. resetStoreVM(this, this.state)
  15. }

相对的,有动态注册模块的需求就有动态卸载模块的需求,Vuex 提供了模块动态卸载功能,在 store 上提供了一个 unregisterModule 的 API

  1. // path模块路径
  2. unregisterModule (path) {
  3. if (typeof path === 'string') path = [path]
  4. if (__DEV__) {
  5. assert(Array.isArray(path), `module path must be a string or an Array.`)
  6. }
  7. // 修剪模块树
  8. this._modules.unregister(path)
  9. this._withCommit(() => {
  10. // 删除state在该路径下的引用
  11. const parentState = getNestedState(this.state, path.slice(0, -1))
  12. Vue.delete(parentState, path[path.length - 1])
  13. })
  14. // 执行resetStore
  15. resetStore(this)
  16. }

unregister
只会移除运行时动态创建的模块

  1. unregister (path) {
  2. const parent = this.get(path.slice(0, -1))
  3. const key = path[path.length - 1]
  4. const child = parent.getChild(key)
  5. if (!child) {
  6. if (__DEV__) {
  7. console.warn(
  8. `[vuex] trying to unregister module '${key}', which is ` +
  9. `not registered`
  10. )
  11. }
  12. return
  13. }
  14. if (!child.runtime) {
  15. return
  16. }
  17. parent.removeChild(key)
  18. }

resetStore

  1. function resetStore (store, hot) {
  2. store._actions = Object.create(null)
  3. store._mutations = Object.create(null)
  4. store._wrappedGetters = Object.create(null)
  5. store._modulesNamespaceMap = Object.create(null)
  6. const state = store.state
  7. // init all modules
  8. installModule(store, state, [], store._modules.root, true)
  9. // reset vm
  10. resetStoreVM(store, state, hot)
  11. }

该方法就是把 store 下的对应存储的 _actions、_mutations、_wrappedGetters 和 _modulesNamespaceMap 都清空,然后重新执行 installModule 安装所有模块以及 resetStoreVM 重置 store._vm

Vuex 提供的一些常用 API 就分析完了,包括数据的存取、语法糖、模块的动态更新等
要理解 Vuex 提供这些 API 都是方便在对 store 做各种操作来完成各种能力,尤其是 mapXXX 的设计,在使用 API 的时候更加方便,这也是今后在设计一些 JavaScript 库的时候,从 API 设计角度中应该学习的方向