Evan You 9 سال پیش
والد
کامیت
08d415c19a

+ 22 - 0
examples/counter-hot/Counter.vue

@@ -0,0 +1,22 @@
+<template>
+  <div>
+    Clicked: {{ count }} times
+    <button @click="increment">+</button>
+    <button @click="decrement">-</button>
+    <button @click="incrementIfOdd">Increment if odd</button>
+    <button @click="incrementAsync">Increment async</button>
+  </div>
+</template>
+
+<script>
+import vuex from './vuex'
+
+export default {
+  computed: {
+    count () {
+      return vuex.state.count
+    }
+  },
+  methods: vuex.actions
+}
+</script>

+ 11 - 0
examples/counter-hot/index.html

@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <title>vuex counter example</title>
+  </head>
+  <body>
+    <counter></counter>
+    <script src="build.js"></script>
+  </body>
+</html>

+ 7 - 0
examples/counter-hot/main.js

@@ -0,0 +1,7 @@
+import Vue from 'vue'
+import Counter from './Counter.vue'
+
+new Vue({
+  el: 'body',
+  components: { Counter }
+})

+ 19 - 0
examples/counter-hot/vuex/actions.js

@@ -0,0 +1,19 @@
+import { INCREMENT, DECREMENT } from './mutation-types'
+
+export default {
+
+  increment: INCREMENT,
+  decrement: DECREMENT,
+
+  incrementIfOdd: () => (dispatch, state) => {
+    if ((state.count + 1) % 2 === 0) {
+      dispatch(INCREMENT)
+    }
+  },
+
+  incrementAsync: () => dispatch => {
+    setTimeout(() => {
+      dispatch(INCREMENT)
+    }, 1000)
+  }
+}

+ 29 - 0
examples/counter-hot/vuex/index.js

@@ -0,0 +1,29 @@
+import Vue from 'vue'
+import Vuex from '../../../src'
+import actions from './actions'
+import mutations from './mutations'
+
+Vue.use(Vuex)
+
+const state = {
+  count: 0
+}
+
+const vuex = new Vuex({
+  state,
+  actions,
+  mutations
+})
+
+if (module.hot) {
+  module.hot.accept(['./actions', './mutations'], () => {
+    const newActions = require('./actions').default
+    const newMutations = require('./mutations').default
+    vuex.hotUpdate({
+      actions: newActions,
+      mutations: newMutations
+    })
+  })
+}
+
+export default vuex

+ 2 - 0
examples/counter-hot/vuex/mutation-types.js

@@ -0,0 +1,2 @@
+export const INCREMENT = 'INCREMENT'
+export const DECREMENT = 'DECREMENT'

+ 10 - 0
examples/counter-hot/vuex/mutations.js

@@ -0,0 +1,10 @@
+import { INCREMENT, DECREMENT } from './mutation-types'
+
+export default {
+  [INCREMENT] (state) {
+    state.count++
+  },
+  [DECREMENT] (state) {
+    state.count--
+  }
+}

+ 1 - 1
examples/shopping-cart/vuex/actions.js

@@ -1,5 +1,5 @@
 import shop from '../api/shop'
-import * as types from './action-types'
+import * as types from './mutation-types'
 
 export const addToCart = types.ADD_TO_CART
 

+ 0 - 0
examples/shopping-cart/vuex/action-types.js → examples/shopping-cart/vuex/mutation-types.js


+ 1 - 1
examples/shopping-cart/vuex/stores/cart.js

@@ -3,7 +3,7 @@ import {
   CHECKOUT_REQUEST,
   CHECKOUT_SUCCESS,
   CHECKOUT_FAILURE
-} from '../action-types'
+} from '../mutation-types'
 
 // initial state
 // shape: [{ id, quantity }]

+ 4 - 1
examples/shopping-cart/vuex/stores/products.js

@@ -1,4 +1,7 @@
-import { RECEIVE_PRODUCTS, ADD_TO_CART } from '../action-types'
+import {
+  RECEIVE_PRODUCTS,
+  ADD_TO_CART
+} from '../mutation-types'
 
 // initial state
 export const productsInitialState = []

+ 1 - 0
package.json

@@ -9,6 +9,7 @@
   ],
   "scripts": {
     "counter": "cd examples/counter && webpack-dev-server --inline --hot --config ../webpack.shared.config.js",
+    "counter-hot": "cd examples/counter-hot && webpack-dev-server --inline --hot --config ../webpack.shared.config.js",
     "todomvc": "cd examples/todomvc && webpack-dev-server --inline --hot --config ../webpack.shared.config.js",
     "cart": "cd examples/shopping-cart && webpack-dev-server --inline --hot --config ../webpack.shared.config.js",
     "prepublish": "babel src --out-dir lib --presets es2015 --plugins add-module-exports"

+ 69 - 29
src/index.js

@@ -18,38 +18,25 @@ export default class Vuex {
     mutations = {},
     middlewares = []
   } = {}) {
-
     // use a Vue instance to store the state tree
     this._vm = new Vue({
       data: state
     })
-
-    // create actions
     this.actions = Object.create(null)
-    actions = Array.isArray(actions)
-      ? mergeObjects(actions)
-      : actions
-    Object.keys(actions).forEach(name => {
-      this.actions[name] = createAction(actions[name], this)
-    })
+    this._setupActions(actions)
+    this._setupMutations(mutations)
+    this._setupMiddlewares(middlewares, state)
+  }
 
-    // mutations
-    this._mutations = Array.isArray(mutations)
-      ? mergeObjects(mutations, true)
-      : mutations
+  /**
+   * Getter for the entire state tree.
+   * Read only.
+   *
+   * @return {Object}
+   */
 
-    // middlewares
-    this._middlewares = [devtoolMiddleware].concat(middlewares)
-    this._needSnapshots = middlewares.some(m => m.snapshot)
-    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)
-      }
-    })
+  get state () {
+    return this._vm._data
   }
 
   /**
@@ -88,13 +75,66 @@ export default class Vuex {
   }
 
   /**
-   * Getter for the entire state tree.
+   * Hot update actions and mutations.
    *
-   * @return {Object}
+   * @param {Object} options
+   *        - {Object} [actions]
+   *        - {Object} [mutations]
    */
 
-  get state () {
-    return this._vm._data
+  hotUpdate ({ actions, mutations } = {}) {
+    if (actions) {
+      this._setupActions(actions, true)
+    }
+    if (mutations) {
+      this._setupMutations(mutations)
+    }
+  }
+
+  _setupActions (actions, hot) {
+    // keep the real action functions in an internal object,
+    // and expose the public object which are just wrapper
+    // functions that point to the real ones. This is so that
+    // the reals ones can be hot reloaded.
+    this._actions = Object.create(null)
+    actions = Array.isArray(actions)
+      ? mergeObjects(actions)
+      : actions
+    Object.keys(actions).forEach(name => {
+      this._actions[name] = createAction(actions[name], this)
+      if (!this.actions[name]) {
+        this.actions[name] = () => this._actions[name]()
+      }
+    })
+    // delete public actions that are no longer present
+    // after a hot reload
+    if (hot) {
+      Object.keys(this.actions).forEach(name => {
+        if (!actions[name]) {
+          delete this.actions[name]
+        }
+      })
+    }
+  }
+
+  _setupMutations (mutations) {
+    this._mutations = Array.isArray(mutations)
+      ? mergeObjects(mutations, true)
+      : mutations
+  }
+
+  _setupMiddlewares (middlewares, state) {
+    this._middlewares = [devtoolMiddleware].concat(middlewares)
+    this._needSnapshots = middlewares.some(m => m.snapshot)
+    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)
+      }
+    })
   }
 }