Skip to content

Commit 743ad19

Browse files
committed
Merge pull request #111 from skyronic/tutorial
Vuex Tutorial
2 parents 55ac7d8 + d5737d2 commit 743ad19

File tree

4 files changed

+267
-0
lines changed

4 files changed

+267
-0
lines changed

docs/en/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
55
- [What is Vuex?](intro.md)
66
- [Getting Started](getting-started.md)
7+
- [Tutorial](tutorial.md)
78
- Core Concepts
89
- [State and Getters](state.md)
910
- [Mutations](mutations.md)

docs/en/tutorial.md

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
# Tutorial
2+
3+
Let's build a very simple app using vuex to understand how to use it. For this example, we're building an app where you press a button, and it increments a counter.
4+
5+
![End Result](tutorial/result.png)
6+
7+
We are using this simple example to explain the concepts, and the problems vuex aims to solve - how to manage a large app which uses several components. Consider if this example used three components:
8+
9+
### `components/App.vue`
10+
11+
The root component, which contains two other child components:
12+
13+
* `Display` to display the current counter value.
14+
* `Increment` which is a button to increment the current value.
15+
16+
```html
17+
<template>
18+
<div>
19+
<Display></Display>
20+
<Increment></Increment>
21+
</div>
22+
</template>
23+
24+
<script>
25+
26+
import Display from './Display.vue'
27+
import Increment from './Increment.vue'
28+
29+
export default {
30+
components: {
31+
Display: Display,
32+
Increment: Increment
33+
}
34+
}
35+
</script>
36+
```
37+
38+
### `components/Display.vue`
39+
40+
```html
41+
<template>
42+
<div>
43+
<h3>Count is 0</h3>
44+
</div>
45+
</template>
46+
47+
<script>
48+
export default {
49+
}
50+
</script>
51+
```
52+
53+
### `components/Increment.vue`
54+
55+
```html
56+
<template>
57+
<div>
58+
<button>Increment +1</button>
59+
</div>
60+
</template>
61+
62+
<script>
63+
export default {
64+
}
65+
</script>
66+
```
67+
68+
### Challenges without vuex
69+
70+
* `Increment` and `Display` aren't aware of each other, and cannot pass messages to each other.
71+
* `App` will have to use events and broadcasts to coordinate the two components.
72+
* Since `App` is coordinating between the two components, they are not re-usable and tightly coupled. Re-structuring the app might break it.
73+
74+
### Vuex "flow"
75+
76+
These are the steps that take place in order:
77+
78+
![Vuex Flow](tutorial/vuex_flow.png)
79+
80+
This might seem a little excessive for incrementing a counter. But do note that these concepts work well in larger applications, improving maintainability and making your app easier to debug and improve in the long run. So let's modify our code to use vuex.
81+
82+
### Step 1: Add a store
83+
84+
The store holds the data for the app. All components read the data from the store. Before we begin, install vuex via npm:
85+
86+
```
87+
$ npm install --save vuex
88+
```
89+
90+
Create a new file in `vuex/store.js`
91+
92+
```js
93+
import Vue from 'vue'
94+
import Vuex from 'vuex'
95+
96+
// Make vue aware of vuex
97+
Vue.use(Vuex)
98+
99+
// We create an object to hold the initial state when
100+
// the app starts up
101+
const state = {
102+
// TODO: Set up our initial state
103+
}
104+
105+
// Create an object storing various mutations. We will write the mutation
106+
const mutations = {
107+
// TODO: set up our mutations
108+
}
109+
110+
// We combine the intial state and the mutations to create a vuex store.
111+
// This store can be linked to our app.
112+
export default new Vuex.Store({
113+
state,
114+
mutations
115+
})
116+
```
117+
118+
We need to make our app aware of this store. To do this we simply need to modify our root component.
119+
120+
Edit `components/App.vue` and add the store.
121+
122+
```js
123+
import Display from './Display.vue'
124+
import Increment from './IncrementButton.vue'
125+
import store from '../vuex/store' // import the store we just created
126+
127+
export default {
128+
components: {
129+
Display: Display,
130+
Increment: Increment
131+
},
132+
store: store // make this and all child components aware of the new store
133+
}
134+
```
135+
136+
> **Tip**: With ES6 and babel you can write it as
137+
>
138+
> components: {
139+
> Display,
140+
> Increment,
141+
> },
142+
> store
143+
144+
### Step 2: Set up the action
145+
146+
The action is a function which is called from the component. Action functions can trigger updates in the store by dispatching the right mutation. An action can also talk to HTTP backends and read other data from the store before dispatching updates.
147+
148+
Create a new file in `vuex/actions.js` with a single function `incrementCounter`
149+
150+
```js
151+
// An action will recieve the store as the first argument.
152+
// Since we are only interested in the dispatch (and optionally the state)
153+
// We can pull those two parameters using the ES6 destructuring feature
154+
export const incrementCounter = function ({ dispatch, state }) {
155+
dispatch('INCREMENT', 1)
156+
}
157+
```
158+
159+
And let's call the action from our `components/Increment.vue` component.
160+
161+
```
162+
<template>
163+
<div>
164+
<button @click='increment'>Increment +1</button>
165+
</div>
166+
</template>
167+
168+
<script>
169+
import { incrementCounter } from '../vuex/actions'
170+
export default {
171+
vuex: {
172+
actions: {
173+
increment: incrementCounter
174+
}
175+
}
176+
}
177+
</script>
178+
```
179+
180+
Notice some interesting things about what we just added.
181+
182+
1. We have a new object `vuex.actions` which includes the new action
183+
2. We didn't specify which store, object, state etc. Vuex wires everything up for us.
184+
3. We can call the action either by using `this.increment()` in any method.
185+
4. We can also call the action using the `@click` parameter making `increment` like any regular vue component method.
186+
5. The action is called `incrementCounter` but we can use any name which is appropriate.
187+
188+
### Step 3: Set up the state and mutation
189+
190+
In our `vuex/actions.js` file we dispatch an `INCREMENT` mutation but we haven't written how to handle it yet. Let's do that now.
191+
192+
Edit `vuex/store.js`
193+
194+
```js
195+
const state = {
196+
// When the app starts, count is set to 0
197+
count: 0
198+
}
199+
200+
const mutations = {
201+
// A mutation recieves the current state as the first argument
202+
// You can make any modifications you want inside this function
203+
INCREMENT (state, amount) {
204+
state.count = state.count + amount
205+
}
206+
}
207+
```
208+
209+
### Step 4: Get the value into the component
210+
211+
Create a new file called `vuex/getters.js`
212+
213+
```js
214+
// This getter is a function which just returns the count
215+
// With ES6 you can also write it as:
216+
// export const getCount = state => state.count
217+
218+
export function getCount (state) {
219+
return state.count
220+
}
221+
```
222+
223+
This function returns a part of the state object which is of interest - the current count. We can now use this getter inside the component.
224+
225+
Edit `components/Display.vue`
226+
227+
```html
228+
<template>
229+
<div>
230+
<h3>Count is {{ counterValue }}</h3>
231+
</div>
232+
</template>
233+
234+
<script>
235+
import { getCount } from '../vuex/getters'
236+
export default {
237+
vuex: {
238+
getters: {
239+
// note that you're passing the function itself, and not the value 'getCount()'
240+
counterValue: getCount
241+
}
242+
}
243+
}
244+
</script>
245+
```
246+
247+
There's a new object `vuex.getters` which requests `counterValue` to be bound to the getter `getCount`. We've chosen different names to demonstrate that you can use the names that make sense in the context of your component, not necessarily the getter name itself.
248+
249+
You might be wondering - why did we choose to use a getter instead of directly accessing the value from the state. This concept is more of a best practice, and is more applicable to a larger app, which presents several distinct advantages:
250+
251+
1. We may want to define getters with computed values (think totals, averages, etc.).
252+
2. Many components in a larger app can use the same getter function.
253+
3. If the value is moved from say `store.count` to `store.counter.value` you'd have to update one getter instead of dozens of components.
254+
255+
These are a few of the benefits of using getters.
256+
257+
### Step 5: Next steps
258+
259+
If you run the application, now you will find it behaves as expected.
260+
261+
To further your understanding of vuex, you can try implementing the following changes to the app, as an exercise.
262+
263+
* Add a decrement button.
264+
* Install [VueJS Devtools](https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd?hl=en) and play with the vuex tools and observe the mutations being applied.
265+
* Add a text input in another component called `IncrementAmount` and enter the amount to increment by. This can be a bit tricky since forms in vuex work slightly differently. Read the [Form Handling](forms.md) section for more details.
266+

docs/en/tutorial/result.png

117 KB
Loading

docs/en/tutorial/vuex_flow.png

601 KB
Loading

0 commit comments

Comments
 (0)