浏览代码

Allow to register module getters dynamically (#253)

* put together initialization code of vm into initStoreVM function

* add getters dynamically by `module` method

* fix incorrect init of getters

* rename _getters -> _wrappedGetters

* fix wrong comment
katashin 8 年之前
父节点
当前提交
ec293dfc21
共有 2 个文件被更改,包括 76 次插入73 次删除
  1. 70 71
      src/index.js
  2. 6 2
      test/unit/test.js

+ 70 - 71
src/index.js

@@ -11,7 +11,6 @@ class Store {
 
     const {
       state = {},
-      modules = {},
       plugins = [],
       strict = false
     } = options
@@ -21,6 +20,7 @@ class Store {
     this._committing = false
     this._actions = Object.create(null)
     this._mutations = Object.create(null)
+    this._wrappedGetters = Object.create(null)
     this._subscribers = []
     this._pendingActions = []
 
@@ -34,16 +34,17 @@ class Store {
       return commit.call(store, type, payload)
     }
 
-    // init state and getters
-    const getters = extractModuleGetters(options.getters, modules)
-    initStoreState(this, state, getters)
+    // strict mode
+    this.strict = strict
+
+    // init internal vm with root state
+    // other options and sub modules will be
+    // initialized in this.module method
+    initStoreVM(this, state, {})
 
     // apply root module
     this.module([], options)
 
-    // strict mode
-    if (strict) enableStrictMode(this)
-
     // apply plugins
     plugins.concat(devtoolPlugin).forEach(plugin => plugin(this))
   }
@@ -67,39 +68,10 @@ class Store {
     if (typeof path === 'string') path = [path]
     assert(Array.isArray(path), `module path must be a string or an Array.`)
 
-    const isRoot = !path.length
-    const {
-      state,
-      actions,
-      mutations,
-      modules
-    } = module
-
-    // set state
-    if (!isRoot && !hot) {
-      const parentState = getNestedState(this.state, path.slice(0, -1))
-      if (!parentState) debugger
-      const moduleName = path[path.length - 1]
-      Vue.set(parentState, moduleName, state || {})
-    }
-
-    if (mutations) {
-      Object.keys(mutations).forEach(key => {
-        this.mutation(key, mutations[key], path)
-      })
-    }
+    initModule(this, path, module, hot)
 
-    if (actions) {
-      Object.keys(actions).forEach(key => {
-        this.action(key, actions[key], path)
-      })
-    }
+    initStoreVM(this, this.state, this._wrappedGetters)
 
-    if (modules) {
-      Object.keys(modules).forEach(key => {
-        this.module(path.concat(key), modules[key], hot)
-      })
-    }
     this._committing = false
   }
 
@@ -203,6 +175,7 @@ class Store {
   hotUpdate (newOptions) {
     this._actions = Object.create(null)
     this._mutations = Object.create(null)
+    this._wrappedGetters = Object.create(null)
     const options = this._options
     if (newOptions.actions) {
       options.actions = newOptions.actions
@@ -210,28 +183,15 @@ class Store {
     if (newOptions.mutations) {
       options.mutations = newOptions.mutations
     }
+    if (newOptions.getters) {
+      options.getters = newOptions.getters
+    }
     if (newOptions.modules) {
       for (const key in newOptions.modules) {
         options.modules[key] = newOptions.modules[key]
       }
     }
     this.module([], options, true)
-
-    // update getters
-    const getters = extractModuleGetters(newOptions.getters, newOptions.modules)
-    if (Object.keys(getters).length) {
-      const oldVm = this._vm
-      initStoreState(this, this.state, getters)
-      if (this.strict) {
-        enableStrictMode(this)
-      }
-      // dispatch changes in all subscribed watchers
-      // to force getter re-evaluation.
-      this._committing = true
-      oldVm.state = null
-      this._committing = false
-      Vue.nextTick(() => oldVm.$destroy())
-    }
   }
 }
 
@@ -239,7 +199,9 @@ function assert (condition, msg) {
   if (!condition) throw new Error(`[vuex] ${msg}`)
 }
 
-function initStoreState (store, state, getters) {
+function initStoreVM (store, state, getters) {
+  const oldVm = store._vm
+
   // bind getters
   store.getters = {}
   const computed = {}
@@ -262,30 +224,67 @@ function initStoreState (store, state, getters) {
     computed
   })
   Vue.config.silent = silent
+
+  // enable strict mode for new vm
+  if (store.strict) {
+    enableStrictMode(store)
+  }
+
+  if (oldVm) {
+    // dispatch changes in all subscribed watchers
+    // to force getter re-evaluation.
+    store._committing = true
+    oldVm.state = null
+    store._committing = false
+    Vue.nextTick(() => oldVm.$destroy())
+  }
 }
 
-function extractModuleGetters (getters = {}, modules = {}, path = []) {
-  if (!path.length) {
-    wrapGetters(getters, getters, path, true)
+function initModule (store, path, module, hot) {
+  const isRoot = !path.length
+  const {
+    state,
+    actions,
+    mutations,
+    getters,
+    modules
+  } = module
+
+  // set state
+  if (!isRoot && !hot) {
+    const parentState = getNestedState(store.state, path.slice(0, -1))
+    if (!parentState) debugger
+    const moduleName = path[path.length - 1]
+    Vue.set(parentState, moduleName, state || {})
   }
-  if (!modules) {
-    return getters
+
+  if (mutations) {
+    Object.keys(mutations).forEach(key => {
+      store.mutation(key, mutations[key], path)
+    })
+  }
+
+  if (actions) {
+    Object.keys(actions).forEach(key => {
+      store.action(key, actions[key], path)
+    })
+  }
+
+  if (getters) {
+    wrapGetters(store._wrappedGetters, getters, path)
+  }
+
+  if (modules) {
+    Object.keys(modules).forEach(key => {
+      initModule(store, path.concat(key), modules[key], hot)
+    })
   }
-  Object.keys(modules).forEach(key => {
-    const module = modules[key]
-    const modulePath = path.concat(key)
-    if (module.getters) {
-      wrapGetters(getters, module.getters, modulePath)
-    }
-    extractModuleGetters(getters, module.modules, modulePath)
-  })
-  return getters
 }
 
-function wrapGetters (getters, moduleGetters, modulePath, force) {
+function wrapGetters (getters, moduleGetters, modulePath) {
   Object.keys(moduleGetters).forEach(getterKey => {
     const rawGetter = moduleGetters[getterKey]
-    if (getters[getterKey] && !force) {
+    if (getters[getterKey]) {
       console.error(`[vuex] duplicate getter key: ${getterKey}`)
       return
     }

+ 6 - 2
test/unit/test.js

@@ -208,12 +208,16 @@ describe('Vuex', () => {
     expect(() => {
       store.module('hi', {
         state: { a: 1 },
-        mutations: { inc: state => state.a++ }
+        mutations: { inc: state => state.a++ },
+        actions: { inc: ({ commit }) => commit('inc') },
+        getters: { a: state => state.a }
       })
     }).not.to.throw()
     expect(store.state.hi.a).to.equal(1)
-    store.commit('inc')
+    expect(store.getters.a).to.equal(1)
+    store.dispatch('inc')
     expect(store.state.hi.a).to.equal(2)
+    expect(store.getters.a).to.equal(2)
   })
 
   it('store injection', () => {