浏览代码

middlewares -> plugins

Evan You 9 年之前
父节点
当前提交
baf0b2104f

+ 2 - 2
examples/chat/vuex/store.js

@@ -1,7 +1,7 @@
 import Vue from 'vue'
 import Vuex from '../../../src'
 import mutations from './mutations'
-import createLogger from '../../../src/middlewares/logger'
+import createLogger from '../../../src/plugins/logger'
 
 Vue.use(Vuex)
 
@@ -33,7 +33,7 @@ export default new Vuex.Store({
     }
   },
   mutations,
-  middlewares: process.env.NODE_ENV !== 'production'
+  plugins: process.env.NODE_ENV !== 'production'
     ? [createLogger()]
     : []
 })

+ 1 - 5
examples/counter/store.js

@@ -12,7 +12,7 @@ const state = {
 // mutations are operations that actually mutates the state.
 // each mutation handler gets the entire state tree as the
 // first argument, followed by additional payload arguments.
-// mutations must be synchronous and can be recorded by middlewares
+// mutations must be synchronous and can be recorded by plugins
 // for debugging purposes.
 const mutations = {
   INCREMENT (state) {
@@ -27,10 +27,6 @@ const mutations = {
 // and the mutations. Because the actions and mutations are just
 // functions that do not depend on the instance itself, they can
 // be easily tested or even hot-reloaded (see counter-hot example).
-//
-// You can also provide middlewares, which is just an array of
-// objects containing some hooks to be called at initialization
-// and after each mutation.
 export default new Vuex.Store({
   state,
   mutations

+ 2 - 2
examples/shopping-cart/vuex/store.js

@@ -2,7 +2,7 @@ import Vue from 'vue'
 import Vuex from '../../../src'
 import cart from './modules/cart'
 import products from './modules/products'
-import createLogger from '../../../src/middlewares/logger'
+import createLogger from '../../../src/plugins/logger'
 
 Vue.use(Vuex)
 Vue.config.debug = true
@@ -15,5 +15,5 @@ export default new Vuex.Store({
     products
   },
   strict: debug,
-  middlewares: debug ? [createLogger()] : []
+  plugins: debug ? [createLogger()] : []
 })

+ 0 - 12
examples/todomvc/vuex/middlewares.js

@@ -1,12 +0,0 @@
-import { STORAGE_KEY } from './store'
-import createLogger from '../../../src/middlewares/logger'
-
-const localStorageMiddleware = {
-  onMutation (mutation, { todos }) {
-    localStorage.setItem(STORAGE_KEY, JSON.stringify(todos))
-  }
-}
-
-export default process.env.NODE_ENV !== 'production'
-  ? [createLogger(), localStorageMiddleware]
-  : [localStorageMiddleware]

+ 12 - 0
examples/todomvc/vuex/plugins.js

@@ -0,0 +1,12 @@
+import { STORAGE_KEY } from './store'
+import createLogger from '../../../src/plugins/logger'
+
+const localStoragePlugin = store => {
+  store.on('mutation', (mutation, { todos }) => {
+    localStorage.setItem(STORAGE_KEY, JSON.stringify(todos))
+  })
+}
+
+export default process.env.NODE_ENV !== 'production'
+  ? [createLogger(), localStoragePlugin]
+  : [localStoragePlugin]

+ 2 - 2
examples/todomvc/vuex/store.js

@@ -1,6 +1,6 @@
 import Vue from 'vue'
 import Vuex from '../../../src'
-import middlewares from './middlewares'
+import plugins from './plugins'
 
 Vue.use(Vuex)
 
@@ -49,5 +49,5 @@ const mutations = {
 export default new Vuex.Store({
   state,
   mutations,
-  middlewares
+  plugins
 })

+ 24 - 79
src/index.js

@@ -1,8 +1,8 @@
 import {
-  mergeObjects, deepClone, isObject,
+  mergeObjects, isObject,
   getNestedState, getWatcher
 } from './util'
-import devtoolMiddleware from './middlewares/devtool'
+import devtoolPlugin from './plugins/devtool'
 import override from './override'
 
 let Vue
@@ -15,7 +15,7 @@ class Store {
    *        - {Object} state
    *        - {Object} actions
    *        - {Object} mutations
-   *        - {Array} middlewares
+   *        - {Array} plugins
    *        - {Boolean} strict
    */
 
@@ -23,13 +23,14 @@ class Store {
     state = {},
     mutations = {},
     modules = {},
-    middlewares = [],
+    plugins = [],
     strict = false
   } = {}) {
     this._getterCacheId = 'vuex_store_' + uid++
     this._dispatching = false
     this._rootMutations = this._mutations = mutations
     this._modules = modules
+    this._events = Object.create(null)
     // bind dispatch to self
     const dispatch = this.dispatch
     this.dispatch = (...args) => {
@@ -53,11 +54,13 @@ class Store {
     Vue.config.silent = silent
     this._setupModuleState(state, modules)
     this._setupModuleMutations(modules)
-    this._setupMiddlewares(middlewares, state)
     // add extra warnings in strict mode
     if (strict) {
       this._setupMutationCheck()
     }
+    // apply plugins
+    devtoolPlugin(this)
+    plugins.forEach(plugin => plugin(this))
   }
 
   /**
@@ -91,25 +94,28 @@ class Store {
       if (type.silent) silent = true
       type = type.type
     }
-    const mutation = this._mutations[type]
+    const handler = this._mutations[type]
     const state = this.state
-    if (mutation) {
+    if (handler) {
       this._dispatching = true
       // apply the mutation
-      if (Array.isArray(mutation)) {
-        mutation.forEach(m => {
+      if (Array.isArray(handler)) {
+        handler.forEach(h => {
           isObjectStyleDispatch
-            ? m(state, payload)
-            : m(state, ...payload)
+            ? h(state, payload)
+            : h(state, ...payload)
         })
       } else {
         isObjectStyleDispatch
-          ? mutation(state, payload)
-          : mutation(state, ...payload)
+          ? handler(state, payload)
+          : handler(state, ...payload)
       }
       this._dispatching = false
       if (!silent) {
-        this._applyMiddlewares(type, payload, isObjectStyleDispatch)
+        const mutation = isObjectStyleDispatch
+          ? payload
+          : { type, payload }
+        this.emit('mutation', mutation, state)
       }
     } else {
       console.warn(`[vuex] Unknown mutation: ${type}`)
@@ -246,71 +252,6 @@ class Store {
     }, { deep: true, sync: true })
     /* eslint-enable no-new */
   }
-
-  /**
-   * Setup the middlewares. The devtools middleware is always
-   * included, since it does nothing if no devtool is detected.
-   *
-   * A middleware can demand the state it receives to be
-   * "snapshots", i.e. deep clones of the actual state tree.
-   *
-   * @param {Array} middlewares
-   * @param {Object} state
-   */
-
-  _setupMiddlewares (middlewares, state) {
-    this._middlewares = [devtoolMiddleware].concat(middlewares)
-    this._needSnapshots = middlewares.some(m => m.snapshot)
-    if (this._needSnapshots) {
-      console.log(
-        '[vuex] One or more of your middlewares are taking state snapshots ' +
-        'for each mutation. Make sure to use them only during development.'
-      )
-    }
-    const initialSnapshot = this._prevSnapshot = this._needSnapshots
-      ? deepClone(state)
-      : null
-    // call init hooks
-    this._middlewares.forEach(m => {
-      if (m.onInit) {
-        m.onInit(m.snapshot ? initialSnapshot : state, this)
-      }
-    })
-  }
-
-  /**
-   * Apply the middlewares on a given mutation.
-   *
-   * @param {String} type
-   * @param {Array} payload
-   * @param {Boolean} isObjectStyleDispatch
-   */
-
-  _applyMiddlewares (type, payload, isObjectStyleDispatch) {
-    const state = this.state
-    const prevSnapshot = this._prevSnapshot
-    let snapshot, clonedPayload
-    if (this._needSnapshots) {
-      snapshot = this._prevSnapshot = deepClone(state)
-      clonedPayload = deepClone(payload)
-    }
-    this._middlewares.forEach(m => {
-      if (m.onMutation) {
-        const mutation = isObjectStyleDispatch
-          ? m.snapshot
-            ? clonedPayload
-            : payload
-          : m.snapshot
-            ? { type, payload: clonedPayload }
-            : { type, payload }
-        if (m.snapshot) {
-          m.onMutation(mutation, snapshot, prevSnapshot, this)
-        } else {
-          m.onMutation(mutation, state, this)
-        }
-      }
-    })
-  }
 }
 
 function install (_Vue) {
@@ -321,6 +262,10 @@ function install (_Vue) {
     return
   }
   Vue = _Vue
+  // reuse Vue's event system
+  ;['on', 'off', 'once', 'emit'].forEach(e => {
+    Store.prototype[e] = Store.prototype['$' + e] = Vue.prototype['$' + e]
+  })
   override(Vue)
 }
 

+ 0 - 19
src/middlewares/devtool.js

@@ -1,19 +0,0 @@
-const hook =
-  typeof window !== 'undefined' &&
-  window.__VUE_DEVTOOLS_GLOBAL_HOOK__
-
-export default {
-  onInit (state, store) {
-    if (!hook) return
-    hook.emit('vuex:init', store)
-    hook.on('vuex:travel-to-state', targetState => {
-      store._dispatching = true
-      store._vm.state = targetState
-      store._dispatching = false
-    })
-  },
-  onMutation (mutation, state) {
-    if (!hook) return
-    hook.emit('vuex:mutation', mutation, state)
-  }
-}

+ 0 - 48
src/middlewares/logger.js

@@ -1,48 +0,0 @@
-// Credits: borrowed code from fcomb/redux-logger
-
-export default function createLogger ({
-  collapsed = true,
-  transformer = state => state,
-  mutationTransformer = mut => mut
-} = {}) {
-  return {
-    snapshot: true,
-    onMutation (mutation, nextState, prevState) {
-      if (typeof console === 'undefined') {
-        return
-      }
-      const time = new Date()
-      const formattedTime = ` @ ${pad(time.getHours(), 2)}:${pad(time.getMinutes(), 2)}:${pad(time.getSeconds(), 2)}.${pad(time.getMilliseconds(), 3)}`
-      const formattedMutation = mutationTransformer(mutation)
-      const message = `mutation ${mutation.type}${formattedTime}`
-      const startMessage = collapsed
-        ? console.groupCollapsed
-        : console.group
-
-      // render
-      try {
-        startMessage.call(console, message)
-      } catch (e) {
-        console.log(message)
-      }
-
-      console.log('%c prev state', 'color: #9E9E9E; font-weight: bold', prevState)
-      console.log('%c mutation', 'color: #03A9F4; font-weight: bold', formattedMutation)
-      console.log('%c next state', 'color: #4CAF50; font-weight: bold', nextState)
-
-      try {
-        console.groupEnd()
-      } catch (e) {
-        console.log('—— log end ——')
-      }
-    }
-  }
-}
-
-function repeat (str, times) {
-  return (new Array(times + 1)).join(str)
-}
-
-function pad (num, maxLength) {
-  return repeat('0', maxLength - num.toString().length) + num
-}

+ 19 - 0
src/plugins/devtool.js

@@ -0,0 +1,19 @@
+const hook =
+  typeof window !== 'undefined' &&
+  window.__VUE_DEVTOOLS_GLOBAL_HOOK__
+
+export default function devtoolPlugin (store) {
+  if (!hook) return
+
+  hook.emit('vuex:init', store)
+
+  hook.on('vuex:travel-to-state', targetState => {
+    store._dispatching = true
+    store._vm.state = targetState
+    store._dispatching = false
+  })
+
+  store.on('mutation', (mutation, state) => {
+    hook.emit('vuex:mutation', mutation, state)
+  })
+}

+ 0 - 23
src/util.js

@@ -25,29 +25,6 @@ export function mergeObjects (arr) {
   }, {})
 }
 
-/**
- * Deep clone an object. Faster than JSON.parse(JSON.stringify()).
- *
- * @param {*} obj
- * @return {*}
- */
-
-export function deepClone (obj) {
-  if (Array.isArray(obj)) {
-    return obj.map(deepClone)
-  } else if (obj && typeof obj === 'object') {
-    var cloned = {}
-    var keys = Object.keys(obj)
-    for (var i = 0, l = keys.length; i < l; i++) {
-      var key = keys[i]
-      cloned[key] = deepClone(obj[key])
-    }
-    return cloned
-  } else {
-    return obj
-  }
-}
-
 /**
  * Check whether the given value is Object or not
  *

+ 12 - 55
test/unit/test.js

@@ -270,7 +270,7 @@ describe('Vuex', () => {
     expect(store.state.four.a).to.equal(7)
   })
 
-  it('middleware', function () {
+  it('plugins', function () {
     let initState
     const mutations = []
     const store = new Vuex.Store({
@@ -282,15 +282,13 @@ describe('Vuex', () => {
           state.a += n
         }
       },
-      middlewares: [
-        {
-          onInit (state) {
-            initState = state
-          },
-          onMutation (mut, state) {
+      plugins: [
+        store => {
+          initState = store.state
+          store.on('mutation', (mut, state) => {
             expect(state).to.equal(store.state)
             mutations.push(mut)
-          }
+          })
         }
       ]
     })
@@ -301,46 +299,7 @@ describe('Vuex', () => {
     expect(mutations[0].payload[0]).to.equal(2)
   })
 
-  it('middleware with snapshot', function () {
-    let initState
-    const mutations = []
-    const store = new Vuex.Store({
-      state: {
-        a: 1
-      },
-      mutations: {
-        [TEST] (state, n) {
-          state.a += n
-        }
-      },
-      middlewares: [
-        {
-          snapshot: true,
-          onInit (state) {
-            initState = state
-          },
-          onMutation (mutation, nextState, prevState) {
-            mutations.push({
-              mutation,
-              nextState,
-              prevState
-            })
-          }
-        }
-      ]
-    })
-    expect(initState).not.to.equal(store.state)
-    expect(initState.a).to.equal(1)
-    store.dispatch(TEST, 2)
-    expect(mutations.length).to.equal(1)
-    expect(mutations[0].mutation.type).to.equal(TEST)
-    expect(mutations[0].mutation.payload[0]).to.equal(2)
-    expect(mutations[0].nextState).not.to.equal(store.state)
-    expect(mutations[0].prevState.a).to.equal(1)
-    expect(mutations[0].nextState.a).to.equal(3)
-  })
-
-  it('middleware should ignore silent mutations', function () {
+  it('plugins should ignore silent mutations', function () {
     let initState
     const mutations = []
     const store = new Vuex.Store({
@@ -352,15 +311,13 @@ describe('Vuex', () => {
           state.a += payload
         }
       },
-      middlewares: [
-        {
-          onInit (state) {
-            initState = state
-          },
-          onMutation (mut, state) {
+      plugins: [
+        store => {
+          initState = store.state
+          store.on('mutation', (mut, state) => {
             expect(state).to.equal(store.state)
             mutations.push(mut)
-          }
+          })
         }
       ]
     })