Vuex
by Gerard Sans | @gerardsans
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/5075430/3qUVl6M-_400x400__1_.jpg)
SANS
GERARD
Google Developer Expert
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/4346612/1_2xKLZ6w42ujjp4d4Be6X6g2-magic__1_.png)
Google Developer Expert
International Speaker
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/4026400/6df7410fac6d0d2822d92d59484ba2d9.jpg)
Spoken at 98 events in 26 countries
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/4348163/Screen_Shot_2017-10-07_at_13.19.24.png)
Blogger
Blogger
Community Leader
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3077823/28311378825_756a12f091_o.jpg)
900
1.5K
Trainer
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3077765/1-Kt1ieFXLC8wuwm-wt0sziQ__1_.jpeg)
Master of Ceremonies
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/5195858/DYU0kTgXUAE6DkD.jpg)
Master of Ceremonies
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/5195880/DWFY2MXXUAAhsns.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/5195882/DWLJrsRW4AAu-FD.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/5195877/DWJfYUcWsAATR2H_bf.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/5195871/DZeu3qBWsAAkTdl__1_.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3091031/Screen_Shot_2016-10-07_at_23.15.32.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3915020/DBE04UbXsAMwmzU.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3915021/34859725642_2cb24ea247_z.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3915023/34636330390_50c844e8ac_z.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3915056/35076212125_bbe45642dd_z.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3915058/34912260892_046f2f4874_z.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/3915061/34688875100_abdbfb8741_z.jpg)
Vuex
- v3.0.1 (Feb 2016)
- 218 contributors
- CLI integration
- 16K stars
- v6.0.1 (Dec 2015)
- 126 contributors
- CLI integration
- 3K stars
- v4 (June 2015)
- 603 contributors
- Inspired by Flux
- 42K stars
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/5075841/639471b0-6bcd-11e7-b808-c12ae105ad03.png)
Vuex
Overview
- State Management for Vue
- Inspired by Redux
- Composable using Modules
- Vue DevTools integration
Components
Actions
Mutations
commit
dispatch
mutate
State
update
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/5092120/grumpy_cat_by_sektor8bit-d7e42ds.gif)
Vuex one-way data flow
Actions
(async)
Mutations
(sync)
Actions vs Mutations
DevTools
Backend
commit
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/5092120/grumpy_cat_by_sektor8bit-d7e42ds.gif)
Packages
vue devtools
vuex
vuex-router-sync
Features
Utilities
Counter
Increment
Decrement
Reset
Total
import Vue from "vue";
import App from "./App";
import store from "./store";
new Vue({
el: "#app",
store,
components: { App },
template: "<App/>"
});
src/app.component.ts
Store Setup
src/main.js
Components
Actions
Mutations
commit
dispatch
mutate
State
update
import Vue from "vue";
import App from "./App";
import store from "./store";
new Vue({
el: "#app",
store, // this.$store.state
components: { App },
template: "<App/>"
});
src/app.component.ts
Store Setup
src/main.js
import actions from "./actions";
import mutations from "./mutations";
export default new Vuex.Store({
actions,
mutations,
state,
});
src/app.component.ts
State
src/store/index.js
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
const state = {
counter: 0
};
export default new Vuex.Store({
state,
});
import mutations from "./mutations";
import actions from "./actions";
export default new Vuex.Store({
state,
mutations,
actions,
});
Components
Actions
Mutations
commit
dispatch
mutate
State
update
export default {
increment(context) {
context.commit("INCREMENT");
},
decrement({ commit }) {
commit("DECREMENT");
},
reset({ commit, dispatch, state, getters }) {
context.commit("RESET");
}
};
src/app.component.ts
Counter Actions
src/store/actions.js
Components
Actions
Mutations
commit
dispatch
mutate
State
update
export default {
INCREMENT(state) {
state.counter += 1;
},
DECREMENT(state) {
state.counter -= 1;
},
RESET(state) {
state.counter = 0;
}
};
src/app.component.ts
Counter Mutations
src/store/mutations.js
Components
Actions
Mutations
commit
dispatch
mutate
State
update
<Counter
@increment="increment" @decrement="decrement" @reset="reset"
/>
export default {
methods: {
increment() {
this.$store.dispatch("increment");
},
reset() {
this.$store.dispatch("reset", { value: 0 });
}
},
}
src/app.component.ts
Dispatching Actions
src/App.vue
We pass events up to container
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/5092120/grumpy_cat_by_sektor8bit-d7e42ds.gif)
<Counter :total="total"
@increment="increment" @decrement="decrement" @reset="reset"
/>
export default {
computed: {
total() {
return this.$store.state.counter;
}
}
}
src/app.component.ts
Updating Component
src/App.vue
We will improve this part later
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/5092120/grumpy_cat_by_sektor8bit-d7e42ds.gif)
<div>
<div class="total">{{total}}</div>
<button @click="$emit('increment')">+</button>
<button @click="$emit('decrement')">-</button>
<button @click="$emit('reset', { value:0 })">C</button>
</div>
export default {
name: "Counter",
props: ["total"]
};
src/app.component.ts
Counter
src/components/Counter.vue
// src/store/actions.js
reset({ commit }, payload) {
commit("RESET", payload);
}
// src/store/mutations.js
RESET(state, { value }) {
state.counter = value;
}
src/app.component.ts
Adding payload
Getters
State
Property
getters
Computed
getters
s
g
g
Vuex Getters
-
Helpers to access Store
-
Avoid Components tight coupling with Store
-
Memoized for performance
import getters from "./getters";
export default new Vuex.Store({
actions,
mutations,
getters,
state,
});
src/app.component.ts
Vuex Store
src/store/index.js
src/app.component.ts
Getters
src/store/getters.js
export default {
total: state => state.counter,
overflow: state => {
return state.counter > state.maximum;
}
};
this.$store.state.counter;
this.$store.state.pagination.page;
Tight coupling
Components
State
Getters: loose coupling
this.$store.getters.total;
this.$store.getters.page;
Components
getters
State
A small change won't break all Components now
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/5092120/grumpy_cat_by_sektor8bit-d7e42ds.gif)
export default {
total: state => {
return state.counter;
},
page: state => {
return state.pagination.page;
}
};
src/app.component.ts
Property Getters
src/store/getters.js
State
Property Selectors
Computed Selectors
s
s
s
Computed Getters
-
Computed values, can use other getters
-
Memoised for performance
-
Default one-slot memoisation
// State
{
todos: [{ id: 1, text: 'Learn Vuex', complete: false }],
currentFilter: "SHOW_ALL"
}
// Visible Todos
[{ id: 1, text: 'Learn Vuex', complete: false }]
src/app.component.ts
Example: visibleTodos Getter
// State
{
todos: [{ id: 1, text: 'Learn Vuex', complete: false }],
currentFilter: "SHOW_COMPLETED"
}
// Visible Todos
[]
const getters = {
todos: state => state.todos,
currentFilter: state => state.currentFilter,
};
const getters = {
todos: state => state.todos,
currentFilter: state => state.currentFilter,
};
Todos Getters
src/store/getters.js
Getter: Visible Todos
const getters = {
visibleTodos: function(state, getters) {
var todos = getters.todos.slice().reverse();
switch (getters.currentFilter) {
case "SHOW_ACTIVE":
return todos.filter(t => !t.done);
case "SHOW_COMPLETED":
return todos.filter(t => t.done);
case "SHOW_ALL":
default:
return todos;
}
}
};
src/store/getters.js
<todo-list
:todos="visibleTodos" :currentFilter="currentFilter"
/>
export default {
computed: {
visibleTodos() {
return this.$store.getters.visibleTodos;
},
currentFilter() {
return this.$store.getters.currentFilter;
}
}
};
src/app.component.ts
Using visibleTodos
src/App.vue
<todo-list
:todos="visibleTodos" :currentFilter="currentFilter"
/>
export default {
computed: {
...mapGetters(['visibleTodos', 'currentFilter'])
}
};
ComponentHelpers
import { mapState } from 'vuex';
export default {
data: () => ({
factor: 12
}),
computed: {
...mapState(['total']),
...mapState({ counter: 'total' }),
formula(state) {
return state.total*this.factor;
}
}
}
src/app.component.ts
Mapping Helpers: mapState
import { mapActions, mapMutations, mapGetters } from 'vuex';
export default {
methods: {
...mapActions(['increment', 'decrement']),
...mapActions({ clear: 'reset' }), // alias
computed: {
...mapGetters({ total: 'total' })
}
}
src/app.component.ts
Other mapping helpers
Modules
Namespaced Module
Extension
Module
Root
Module
Modules
import cart from './modules/cart'
import products from './modules/products'
export default new Vuex.Store({
modules: {
cart,
products
},
})
src/app.component.ts
Store Setup
src/store/index.js
export default {
namespaced: true,
state: {
items: [],
checkoutStatus: null
},
actions,
mutations
getters,
}
src/app.component.ts
Cart Module
src/store/modules/cart.js
export default {
namespaced: true,
state: {
all: [],
},
actions,
mutations
getters,
}
src/app.component.ts
Products Module
src/store/modules/products.js
<Product v-for="product in products"
@click="addProductToCart(product)"
/>
export default {
computed: mapState({
products: state => state.products.all
}),
methods: mapActions('cart', [
'addProductToCart'
]),
created () {
this.$store.dispatch('products/getAllProducts')
}
}
src/app.component.ts
ProductList Component
Reusing stores
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/5078604/Screen_Shot_2018-07-01_at_23.11.35.png)
{
counter: 7
}
Single Counter
dispatch("increment");
State
Action
User clicks
{
counter: 8
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/5092120/grumpy_cat_by_sektor8bit-d7e42ds.gif)
Mutation
commit("increment");
Multiple Counters
{
"a": { counter: 7 },
"b": { counter: 3 },
}
a
b
State
User clicks
{
"a": { counter: 7 },
"b": { counter: 2 },
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/5092120/grumpy_cat_by_sektor8bit-d7e42ds.gif)
dispatch('b/decrement');
Action
Mutation in Module B
commit("decrement");
import counter1 from "./modules/counter";
import counter2 from "./modules/counter";
export default new Vuex.Store({
modules: {
a: counter1,
b: counter2
}
});
src/app.component.ts
Store Setup
src/store/index.js
export default {
namespaced: true,
state: () => ({
counter: 0
}),
// same actions, mutations and getters
};
src/app.component.ts
Store Setup
src/store/modules/counter.js
More
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/5201346/a-vxWBFi_400x400.jpg)
@sarah_edo
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/5198788/phhp5grt_400x400.jpg)
Blake Newman
Sarah Drasner
@blakenewman
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/5187109/vueconf.773fab9.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/5187109/vueconf.773fab9.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/345677/images/5187109/vueconf.773fab9.png)
Vuex
By Gerard Sans
Vuex
State Management is key to build modern Web Apps
- 2,609