Skip to content

Commit 2d34db8

Browse files
committed
[docs] structure
1 parent 19dd25d commit 2d34db8

File tree

3 files changed

+86
-82
lines changed

3 files changed

+86
-82
lines changed

docs/en/structure.md

Lines changed: 54 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
# Application Structure
22

3-
Vuex doesn't really restrict how you structure your code. Rather, it enforces a set of opinions:
3+
Vuex doesn't really restrict how you structure your code. Rather, it enforces a set of high-level principles:
44

5-
1. Application state lives in a single object.
6-
2. Only mutation handlers can mutate the state.
7-
3. Mutations must be synchronous, and the only side effects they produce should be state mutation.
8-
4. All asynchronous logic such as data fetching should be performed in actions.
5+
1. Application state is held in the store, as a single object.
96

10-
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.
7+
2. The only way to mutate the state is by dispatching mutations on the store.
8+
9+
3. Mutations must be synchronous, and the only side effects they produce should be mutating the state.
10+
11+
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.
12+
13+
5. Components use getters to retrieve state from the store, and call actions to mutate the state.
14+
15+
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.
1116

1217
### Simple Project
1318

14-
For a simple project, we can simply separate **actions** and **mutations** into respective files:
19+
For a simple project, we can simply define the **store** and the **actions** in respective files:
1520

1621
``` bash
1722
.
@@ -20,17 +25,18 @@ For a simple project, we can simply separate **actions** and **mutations** into
2025
├── components
2126
│   ├── App.vue
2227
│   └── ...
23-
└── store
24-
├── index.js # exports the vuex store
25-
├── actions.js # exports all actions
26-
└── mutations.js # exports all mutations
28+
└── vuex
29+
├── store.js # exports the store (with initial state and mutations)
30+
└── actions.js # exports all actions
2731
```
2832

29-
For an actual example, check out the [TodoMVC example](https://github.com/vuejs/vuex/tree/master/examples/todomvc).
33+
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).
34+
35+
Alternatively, you can also split out mutations into its own file.
3036

3137
### Medium to Large Project
3238

33-
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:
39+
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:
3440

3541
``` bash
3642
├── index.html
@@ -40,75 +46,71 @@ For any non-trivial app, we probably want to further split Vuex-related code int
4046
├── components
4147
│   ├── App.vue
4248
│   └── ...
43-
└── store
44-
├── actions.js # exports all actions
45-
├── index.js
46-
├── modules
47-
│   ├── cart.js # state and mutations for cart
48-
   ── products.js # state and mutations for products
49-
└── mutation-types.js # constants
49+
└── vuex
50+
├── actions.js # exports all actions
51+
├── store.js # where we assemble modules and export the store
52+
├── mutation-types.js # constants
53+
── modules
54+
   ── cart.js # state and mutations for cart
55+
   └── products.js # state and mutations for products
5056
```
5157

5258
A typical module looks like this:
5359

5460
``` js
5561
// vuex/modules/products.js
56-
import { RECEIVE_PRODUCTS, ADD_TO_CART } from '../mutation-types'
62+
import {
63+
RECEIVE_PRODUCTS,
64+
ADD_TO_CART
65+
} from '../mutation-types'
5766

5867
// initial state
59-
export const productsInitialState = []
68+
const state = {
69+
all: []
70+
}
6071

6172
// mutations
62-
export const productsMutations = {
73+
const mutations = {
6374
[RECEIVE_PRODUCTS] (state, products) {
64-
state.products = products
75+
state.all = products
6576
},
6677

67-
[ADD_TO_CART] ({ products }, productId) {
68-
const product = products.find(p => p.id === productId)
69-
if (product.inventory > 0) {
70-
product.inventory--
71-
}
78+
[ADD_TO_CART] (state, productId) {
79+
state.all.find(p => p.id === productId).inventory--
7280
}
7381
}
82+
83+
export default {
84+
state,
85+
mutations
86+
}
7487
```
7588

76-
And in `store/index.js`, we "assemble" multiple modules together to create the Vuex instance:
89+
And in `vuex/store.js`, we "assemble" multiple modules together to create the Vuex instance:
7790

7891
``` js
92+
// vuex/store.js
7993
import Vue from 'vue'
8094
import Vuex from '../../../src'
81-
import * as actions from './actions'
8295
// import parts from modules
83-
import { cartInitialState, cartMutations } from './modules/cart'
84-
import { productsInitialState, productsMutations } from './modules/products'
96+
import cart from './modules/cart'
97+
import products from './modules/products'
8598

8699
Vue.use(Vuex)
87100

88101
export default new Vuex.Store({
89-
// ...
90-
// combine sub-trees into root state
91-
state: {
92-
cart: cartInitialState,
93-
products: productsInitialState
94-
},
95-
// mutations can be an array of mutation definition objects
96-
// from multiple modules
97-
mutations: [cartMutations, productsMutations]
102+
// combine sub modules
103+
modules: {
104+
cart,
105+
products
106+
}
98107
})
99108
```
100109

101-
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.
102-
103-
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.
104-
105-
For an actual example, check out the [Shopping Cart Example](https://github.com/vuejs/vuex/tree/master/examples/shopping-cart).
110+
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.
106111

107-
### Extracting Shared Computed Getters
112+
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.
108113

109-
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:
114+
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.
110115

111116
For an actual example, check out the [Shopping Cart Example](https://github.com/vuejs/vuex/tree/master/examples/shopping-cart).
112-
For an actual example with hot reload API, check out the [Counter Hot Example](https://github.com/vuejs/vuex/tree/master/examples/counter-hot).
113-
114-
For more information, check out the [Getters documentation](getters.md)

examples/todomvc/vuex/mutations.js

Lines changed: 0 additions & 30 deletions
This file was deleted.

examples/todomvc/vuex/store.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,42 @@ import middlewares from './middlewares'
66
Vue.use(Vuex)
77

88
export const STORAGE_KEY = 'todos-vuejs'
9+
910
const state = {
1011
todos: JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]')
1112
}
1213

14+
const mutations = {
15+
ADD_TODO (state, text) {
16+
state.todos.unshift({
17+
text: text,
18+
done: false
19+
})
20+
},
21+
22+
DELETE_TODO (state, todo) {
23+
state.todos.$remove(todo)
24+
},
25+
26+
TOGGLE_TODO (state, todo) {
27+
todo.done = !todo.done
28+
},
29+
30+
EDIT_TODO (state, todo, text) {
31+
todo.text = text
32+
},
33+
34+
TOGGLE_ALL (state, done) {
35+
state.todos.forEach((todo) => {
36+
todo.done = done
37+
})
38+
},
39+
40+
CLEAR_COMPLETED (state) {
41+
state.todos = state.todos.filter(todo => !todo.done)
42+
}
43+
}
44+
1345
export default new Vuex.Store({
1446
state,
1547
mutations,

0 commit comments

Comments
 (0)