10000 docs: review rest of guide · padcom/vuejs.org@977ee4a · GitHub
[go: up one dir, main page]

Skip to content

Commit 977ee4a

Browse files
committed
docs: review rest of guide
1 parent 64a0785 commit 977ee4a

File tree

5 files changed

+53
-167
lines changed

5 files changed

+53
-167
lines changed

docs/en/forms.md

Lines changed: 14 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ The "Vuex way" to deal with it is binding the `<input>`'s value and call an acti
1515
```
1616
``` js
1717
// ...
18-
vuex: {
19-
getters: {
18+
computed: {
19+
...mapState({
2020
message: state => state.obj.message
21-
},
22-
actions: {
23-
updateMessage: ({ dispatch }, e) => {
24-
dispatch('UPDATE_MESSAGE', e.target.value)
25-
}
21+
})
22+
},
23+
methods: {
24+
updateMessage (e) {
25+
this.$store.commit('updateMessage', e.target.value)
2626
}
2727
}
2828
```
@@ -32,41 +32,27 @@ And here's the mutation handler:
3232
``` js
3333
// ...
3434
mutations: {
35-
UPDATE_MESSAGE (state, message) {
35+
updateMessage (state, message) {
3636
state.obj.message = message
3737
}
3838
}
3939
```
4040

41-
Admittedly, this is quite a bit more verbose than a simple `v-model`, but such is the cost of making state changes explicit and track-able. At the same time, do note that Vuex doesn't demand putting all your state inside a Vuex store - if you do not wish to track the mutations for form interactions at all, you can simply keep the form state outside of Vuex as component local state, which allows you to freely leverage `v-model`.
41+
### Two-way Computed Property
4242

43-
Yet an alternative approach to leverage `v-model` with state in Vuex store is to use a computed property providing a setter in the component, with all the advantages of param attributes such as lazy, number and debounce.
43+
Admittedly, the above is quite a bit more verbose than `v-model` + local state, and we lose some of the useful features from `v-model` as well. An alternative approach is using a two-way computed property with a setter:
4444

45-
``` html
46-
<input v-model="thisMessage">
47-
```
4845
``` js
4946
// ...
50-
vuex: {
51-
getters: {
52-
message: state => state.obj.message
53-
},
54-
actions: {
55-
updateMessage: ({ dispatch }, value) => {
56-
dispatch('UPDATE_MESSAGE', value)
57-
}
58-
}
59-
},
6047
computed: {
61-
thisMessage: {
48+
message: {
6249
get () {
63-
return this.message
50+
return this.$store.state.obj.message
6451
},
65-
set (val) {
66-
this.updateMessage(val)
52+
set (value) {
53+
this.$store.commit('updateMessage', value)
6754
}
6855
}
6956
}
7057
```
7158

72-
The mutation remains the same.

docs/en/hot-reload.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,4 @@ if (module.hot) {
4141
}
4242
```
4343

44-
You don't need to do anything specific for actions and getters. Webpack's hot module replacement system will "bubble" changes up the dependency chain - and changes in actions and getters will bubble up to the Vue components that imported them. Because Vue components loaded via `vue-loader` are automatically hot-reloadable, these affected components will hot-reload themselves and use the updated actions and getters.
44+
Checkout the [counter-hot example](https://github.com/vuejs/vuex/tree/master/examples/counter-hot) to play with hot-reload.

docs/en/plugins.md

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ const myPlugin = store => {
77
// called when the store is initialized
88
store.subscribe((mutation, state) => {
99
// called after every mutation.
10-
// The mutation comes in the format of { type, payload } for normal
11-
// dispatches, and will be the original mutation object for object-style
12-
// dispatches.
10+
// The mutation comes in the format of { type, payload }.
1311
})
1412
}
1513
```
@@ -23,17 +21,17 @@ const store = new Vuex.Store({
2321
})
2422
```
2523

26-
### Dispatching Inside Plugins
24+
### Committing Mutations Inside Plugins
2725

28-
Plugins are not allowed to directly mutate state - similar to your components, they can only trigger changes by dispatching mutations.
26+
Plugins are not allowed to directly mutate state - similar to your components, they can only trigger changes by committing mutations.
2927

30-
By dispatching mutations, a plugin can be used to sync a data source to the store. For example, to sync a websocket data source to the store (this is just a contrived example, in reality the `createPlugin` function can take some additional options for more complex tasks):
28+
By committing mutations, a plugin can be used to sync a data source to the store. For example, to sync a websocket data source to the store (this is just a contrived example, in reality the `createPlugin` function can take some additional options for more complex tasks):
3129

3230
``` js
3331
export default function createWebSocketPlugin (socket) {
3432
return store => {
3533
socket.on('data', data => {
36-
store.dispatch('RECEIVE_DATA', data)
34+
store.commit('receiveData', data)
3735
})
3836
store.subscribe(mutation => {
3937
if (mutation.type === 'UPDATE_DATA') {
@@ -87,10 +85,12 @@ The plugin will be used by default. For production, you will need [DefinePlugin]
8785

8886
### Built-in Logger Plugin
8987

88+
> If you are using [vue-devtools](https://github.com/vuejs/vue-devtools) you probably don't need this.
89+
9090
Vuex comes with a logger plugin for common debugging usage:
9191

9292
``` js
93-
import createLogger from 'vuex/logger'
93+
import createLogger from 'vuex/dist/logger'
9494

9595
const store = new Vuex.Store({
9696
plugins: [createLogger()]
@@ -115,4 +115,6 @@ const logger = createLogger({
115115
})
116116
```
117117

118+
The logger file can also be included directly via a `<script>` tag, and will expose the `createVuexLogger` function globally.
119+
118120
Note the logger plugin takes state snapshots, so use it only during development.

docs/en/structure.md

Lines changed: 12 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -2,41 +2,15 @@
22

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

5-
1. Application state is held in the store, as a single object.
5+
1. Application-level state is centralized in the store.
66

7-
2. The only way to mutate the state is by dispatching mutations on the store.
7+
2. The only way to mutate the state is by committing **mutations**, which are synchronous transactions.
88

9-
3. Mutations must be synchronous, and the only side effects they produce should be mutating the state.
9+
3. Asynchronous logic should be encapsulated in, and can be composed with **actions**.
1010

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.
11+
As long as you follow these rules, it's up to you how to structure your project. If your store file gets too big, simply start splitting the actions, mutations and getters into separate files.
1212

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.
16-
17-
### Simple Project
18-
19-
For a simple project, we can simply define the **store** and the **actions** in respective files:
20-
21-
``` bash
22-
.
23-
├── index.html
24-
├── main.js
25-
├── components
26-
│   ├── App.vue
27-
│   └── ...
28-
└── vuex
29-
├── store.js # exports the store (with initial state and mutations)
30-
└── actions.js # exports all actions
31-
```
32-
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.
36-
37-
### Medium to Large Project
38-
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:
13+
For any non-trivial app, we will likely need to leverage modules. Here's an example project structure:
4014

4115
``` bash
4216
├── index.html
@@ -46,91 +20,13 @@ For any non-trivial app, we probably want to further split Vuex-related code int
4620
├── components
4721
│   ├── App.vue
4822
│   └── ...
49-
└── vuex
50-
├── actions.js # exports all actions
51-
├── store.js # where we assemble modules and export the store
52-
├── mutation-types.js # constants
23+
└── store
24+
├── index.js # where we assemble modules and export the store
25+
├── actions.js # root actions
26+
├── mutations.js # root mutations
5327
└── modules
54-
   ├── cart.js # state and mutations for cart
55-
   └── products.js # state and mutations for products
56-
```
57-
58-
A typical module looks like this:
59-
60-
``` js
61-
// vuex/modules/products.js
62-
import {
63-
RECEIVE_PRODUCTS,
64-
ADD_TO_CART
65-
} from '../mutation-types'
66-
67-
// initial state
68-
const state = {
69-
all: []
70-
}
71-
72-
// mutations
73-
const mutations = {
74-
[RECEIVE_PRODUCTS] (state, products) {
75-
state.all = products
76-
},
77-
78-
[ADD_TO_CART] (state, productId) {
79-
state.all.find(p => p.id === productId).inventory--
80-
}
81-
}
82-
83-
export default {
84-
state,
85-
mutations
86-
}
87-
```
88-
89-
And in `vuex/store.js`, we "assemble" multiple modules together to create the Vuex instance:
90-
91-
``` js
92-
// vuex/store.js
93-
import Vue from 'vue'
94-
import Vuex from '../../../src'
95-
// import parts from modules
96-
import cart from './modules/cart'
97-
import products from './modules/products'
98-
99-
Vue.use(Vuex)
100-
101-
export default new Vuex.Store({
102-
// combine sub modules
103-
modules: {
104-
cart,
105-
products
106-
}
107-
})
28+
   ├── cart.js # cart module
29+
   └── products.js # products module
10830
```
10931

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 receive the sub-state-tree they are associated with**. So mutations defined in the `cart` module will receive `store.state.cart` as their first argument.
111-
112-
The root of the sub-state-tree is irreplaceable inside the module itself. For example this won't work:
113-
114-
``` js
115-
const mutations = {
116-
SOME_MUTATION (state) {
117-
state = { ... }
118-
}
119-
}
120-
```
121-
122-
Instead, always store actual state as a property of the sub-tree root:
123-
124-
``` js
125-
const mutations = {
126-
SOME_MUTATION (state) {
127-
state.value = { ... }
128-
}
129-
}
130-
```
131-
132-
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.
133-
134-
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.
135-
136-
For an example, check out the [Shopping Cart Example](https://github.com/vuejs/vuex/tree/master/examples/shopping-cart).
32+
As a reference, check out the [Shopping Cart Example](https://github.com/vuejs/vuex/tree/master/examples/shopping-cart).

docs/en/testing.md

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ Example testing a mutation using Mocha + Chai (you can use any framework/asserti
2222

2323
``` js
2424
// mutations.js
25-
export const INCREMENT = state => state.count++
25+
export const mutations = {
26+
increment: state => state.count++
27+
}
2628
```
2729

2830
``` js
@@ -31,14 +33,14 @@ import { expect } from 'chai'
3133
import { mutations } from './store'
3234

3335
// destructure assign mutations
34-
const { INCREMENT } = mutations
36+
const { increment } = mutations
3537

3638
describe('mutations', () => {
3739
it('INCREMENT', () => {
3840
// mock state
3941
const state = { count: 0 }
4042
// apply mutation
41-
INCREMENT(state)
43+
increment(state)
4244
// assert result
4345
expect(state.count).to.equal(1)
4446
})
@@ -86,10 +88,11 @@ const actions = actionsInjector({
8688
// helper for testing action with expected mutations
8789
const testAction = (action, args, state, expectedMutations, done) => {
8890
let count = 0
89-
// mock dispatch
90-
const dispatch = (name, ...payload) => {
91+
92+
// mock commit
93+
const commit = (type, payload) => {
9194
const mutation = expectedMutations[count]
92-
expect(mutation.name).to.equal(name)
95+
expect(mutation.type).to.equal(type)
9396
if (payload) {
9497
expect(mutation.payload).to.deep.equal(payload)
9598
}
@@ -98,8 +101,9 @@ const testAction = (action, args, state, expectedMutations, done) => {
98101
done()
99102
}
100103
}
104+
101105
// call the action with mocked store and arguments
102-
action({dispatch, state}, ...args)
106+
action({ commit, state }, ...args)
103107

104108
// check if no mutations should have been dispatched
105109
if (expectedMutations.length === 0) {
@@ -111,8 +115,8 @@ const testAction = (action, args, state, expectedMutations, done) => {
111115
describe('actions', () => {
112116
it('getAllProducts', done => {
113117
testAction(actions.getAllProducts, [], {}, [
114-
{ name: 'REQUEST_PRODUCTS' },
115-
{ name: 'RECEIVE_PRODUCTS', payload: [ /* mocked response */ ] }
118+
{ type: 'REQUEST_PRODUCTS' },
119+
{ type: 'RECEIVE_PRODUCTS', payload: { /* mocked response */ } }
116120
], done)
117121
})
118122
})
@@ -124,9 +128,10 @@ If your mutations and actions are written properly, the tests should have no dir
124128

125129
#### Running in Node
126130

127-
Create the following webpack config:
131+
Create the following webpack config (together with proper [`.babelrc`](https://babeljs.io/docs/usage/babelrc/)):
128132

129133
``` js
134+
// webpack.config.js
130135
module.exports = {
131136
entry: './test.js',
132137
output: {
@@ -141,9 +146,6 @@ module.exports = {
141146
exclude: /node_modules/
142147
}
143148
]
144-
},
145-
babel: {
146-
presets: ['es2015']
147149
}
148150
}
149151
```
@@ -164,4 +166,4 @@ mocha test-bundle.js
164166

165167
#### Running in Browser with Karma + karma-webpack
166168

167-
Consult the setup in [vue-loader documentation](http://vuejs.github.io/vue-loader/workflow/testing.html).
169+
Consult the setup in [vue-loader documentation](http://vue-loader.vuejs.org/en/workflow/testing.html).

0 commit comments

Comments
 (0)
0