瀏覽代碼

[docs] structure

Evan You 9 年之前
父節點
當前提交
2d34db8876
共有 3 個文件被更改,包括 86 次插入82 次删除
  1. 54 52
      docs/en/structure.md
  2. 0 30
      examples/todomvc/vuex/mutations.js
  3. 32 0
      examples/todomvc/vuex/store.js

+ 54 - 52
docs/en/structure.md

@@ -1,17 +1,22 @@
 # Application Structure
 
-Vuex doesn't really restrict how you structure your code. Rather, it enforces a set of opinions:
+Vuex doesn't really restrict how you structure your code. Rather, it enforces a set of high-level principles:
 
-1. Application state lives in a single object.
-2. Only mutation handlers can mutate the state.
-3. Mutations must be synchronous, and the only side effects they produce should be state mutation.
-4. All asynchronous logic such as data fetching should be performed in actions.
+1. Application state is held in the store, as a single object.
 
-The nice thing about Vuex actions and mutations is that **they are just functions**. As long as you follow these rules, it's up to you how to structure your project. The simplest Vuex instance can even be declared [in a single file](https://github.com/vuejs/vuex/blob/master/examples/counter/vuex.js)! However, this is unlikely to suffice for any serious project, so here are some recommended structures depending on the scale of your app.
+2. The only way to mutate the state is by dispatching mutations on the store.
+
+3. Mutations must be synchronous, and the only side effects they produce should be mutating the state.
+
+4. We can expose a more expressive state mutation API by defining actions. Actions can encapsulate asynchronous logic such as data fetching, and the only side effects they produce should be dispatching mutations.
+
+5. Components use getters to retrieve state from the store, and call actions to mutate the state.
+
+The nice thing about Vuex mutations, actions and getters is that **they are all just functions**. As long as you follow these rules, it's up to you how to structure your project. However, it's nice to have some conventions so that you can instantly become familiar with another project that uses Vuex, so here are some recommended structures depending on the scale of your app.
 
 ### Simple Project
 
-For a simple project, we can simply separate **actions** and **mutations** into respective files:
+For a simple project, we can simply define the **store** and the **actions** in respective files:
 
 ``` bash
 .
@@ -20,17 +25,18 @@ For a simple project, we can simply separate **actions** and **mutations** into
 ├── components
 │   ├── App.vue
 │   └── ...
-└── store
-    ├── index.js     # exports the vuex store
-    ├── actions.js   # exports all actions
-    └── mutations.js # exports all mutations
+└── vuex
+    ├── store.js     # exports the store (with initial state and mutations)
+    └── actions.js   # exports all actions
 ```
 
-For an actual example, check out the [TodoMVC example](https://github.com/vuejs/vuex/tree/master/examples/todomvc).
+For an actual example, check out the [Counter example](https://github.com/vuejs/vuex/tree/master/examples/counter) or the [TodoMVC example](https://github.com/vuejs/vuex/tree/master/examples/todomvc).
+
+Alternatively, you can also split out mutations into its own file.
 
 ### Medium to Large Project
 
-For any non-trivial app, we probably want to further split Vuex-related code into multiple "modules" (roughly comparable to "stores" in vanilla Flux), each dealing with a specific domain of our app. Each module would be managing a sub-tree of the state, exporting the initial state for that sub-tree and all mutations that operate on that sub-tree:
+For any non-trivial app, we probably want to further split Vuex-related code into multiple "modules" (roughly comparable to "stores" in vanilla Flux, and "reducers" in Redux), each dealing with a specific domain of our app. Each module would be managing a sub-tree of the state, exporting the initial state for that sub-tree and all mutations that operate on that sub-tree:
 
 ``` bash
 ├── index.html
@@ -40,75 +46,71 @@ For any non-trivial app, we probably want to further split Vuex-related code int
 ├── components
 │   ├── App.vue
 │   └── ...
-└── store
-    ├── actions.js # exports all actions
-    ├── index.js
-    ├── modules
-    │   ├── cart.js       # state and mutations for cart
-    │   └── products.js   # state and mutations for products
-    └── mutation-types.js # constants
+└── vuex
+    ├── actions.js        # exports all actions
+    ├── store.js          # where we assemble modules and export the store
+    ├── mutation-types.js # constants
+    └── modules
+        ├── cart.js       # state and mutations for cart
+        └── products.js   # state and mutations for products
 ```
 
 A typical module looks like this:
 
 ``` js
 // vuex/modules/products.js
-import { RECEIVE_PRODUCTS, ADD_TO_CART } from '../mutation-types'
+import {
+  RECEIVE_PRODUCTS,
+  ADD_TO_CART
+} from '../mutation-types'
 
 // initial state
-export const productsInitialState = []
+const state = {
+  all: []
+}
 
 // mutations
-export const productsMutations = {
+const mutations = {
   [RECEIVE_PRODUCTS] (state, products) {
-    state.products = products
+    state.all = products
   },
 
-  [ADD_TO_CART] ({ products }, productId) {
-    const product = products.find(p => p.id === productId)
-    if (product.inventory > 0) {
-      product.inventory--
-    }
+  [ADD_TO_CART] (state, productId) {
+    state.all.find(p => p.id === productId).inventory--
   }
 }
+
+export default {
+  state,
+  mutations
+}
 ```
 
-And in `store/index.js`, we "assemble" multiple modules together to create the Vuex instance:
+And in `vuex/store.js`, we "assemble" multiple modules together to create the Vuex instance:
 
 ``` js
+// vuex/store.js
 import Vue from 'vue'
 import Vuex from '../../../src'
-import * as actions from './actions'
 // import parts from modules
-import { cartInitialState, cartMutations } from './modules/cart'
-import { productsInitialState, productsMutations } from './modules/products'
+import cart from './modules/cart'
+import products from './modules/products'
 
 Vue.use(Vuex)
 
 export default new Vuex.Store({
-  // ...
-  // combine sub-trees into root state
-  state: {
-    cart: cartInitialState,
-    products: productsInitialState
-  },
-  // mutations can be an array of mutation definition objects
-  // from multiple modules
-  mutations: [cartMutations, productsMutations]
+  // combine sub modules
+  modules: {
+    cart,
+    products
+  }
 })
 ```
 
-Since all modules simply export objects and functions, they are quite easy to test and maintain. You are also free to alter the patterns used here to find a structure that fits your preference.
-
-Note that we do not put actions into modules, because a single action may dispatch mutations that affect multiple modules. It's also a good idea to decouple actions from the state shape and the implementation details of mutations for better separation of concerns. If the actions file gets too large, we can turn it into a folder and split out the implementations of long async actions into individual files.
-
-For an actual example, check out the [Shopping Cart Example](https://github.com/vuejs/vuex/tree/master/examples/shopping-cart).
+Here, `cart` module's initial state will be attached to the root state tree as `store.state.cart`. In addition, **all the mutations defined in a sub-module only receives the sub-state-tree they are associated with**. So mutations defined in the `cart` module will receive `store.state.cart` as their first argument.
 
-### Extracting Shared Computed Getters
+Since all modules simply export objects and functions, they are quite easy to test and maintain, and can be hot-reloaded. You are also free to alter the patterns used here to find a structure that fits your preference.
 
-In large projects, it's possible that multiple components will need the same computed property based on Vuex state. Since computed getters are just functions, you can split them out into a separate file so that they can be shared in any component via the store:
+Note that we do not put actions into modules, because a single action may dispatch mutations that affect multiple modules. It's also a good idea to decouple actions from the state shape and the implementation details of mutations for better separation of concerns. If the actions file gets too large, we can turn it into a folder and split out the implementations of long async actions into individual files.
 
 For an actual example, check out the [Shopping Cart Example](https://github.com/vuejs/vuex/tree/master/examples/shopping-cart).
-For an actual example with hot reload API, check out the [Counter Hot Example](https://github.com/vuejs/vuex/tree/master/examples/counter-hot).
-
-For more information, check out the [Getters documentation](getters.md)

+ 0 - 30
examples/todomvc/vuex/mutations.js

@@ -1,30 +0,0 @@
-export default {
-  ADD_TODO (state, text) {
-    state.todos.unshift({
-      text: text,
-      done: false
-    })
-  },
-
-  DELETE_TODO (state, todo) {
-    state.todos.$remove(todo)
-  },
-
-  TOGGLE_TODO (state, todo) {
-    todo.done = !todo.done
-  },
-
-  EDIT_TODO (state, todo, text) {
-    todo.text = text
-  },
-
-  TOGGLE_ALL (state, done) {
-    state.todos.forEach((todo) => {
-      todo.done = done
-    })
-  },
-
-  CLEAR_COMPLETED (state) {
-    state.todos = state.todos.filter(todo => !todo.done)
-  }
-}

+ 32 - 0
examples/todomvc/vuex/store.js

@@ -6,10 +6,42 @@ import middlewares from './middlewares'
 Vue.use(Vuex)
 
 export const STORAGE_KEY = 'todos-vuejs'
+
 const state = {
   todos: JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]')
 }
 
+const mutations = {
+  ADD_TODO (state, text) {
+    state.todos.unshift({
+      text: text,
+      done: false
+    })
+  },
+
+  DELETE_TODO (state, todo) {
+    state.todos.$remove(todo)
+  },
+
+  TOGGLE_TODO (state, todo) {
+    todo.done = !todo.done
+  },
+
+  EDIT_TODO (state, todo, text) {
+    todo.text = text
+  },
+
+  TOGGLE_ALL (state, done) {
+    state.todos.forEach((todo) => {
+      todo.done = done
+    })
+  },
+
+  CLEAR_COMPLETED (state) {
+    state.todos = state.todos.filter(todo => !todo.done)
+  }
+}
+
 export default new Vuex.Store({
   state,
   mutations,