Vuex Modules
Modularizing Vuex
Vuex Modules
A Vuex module allows you to subdivide the Vuex store such that each module contains it's own state, actions, mutations and getters
Machine
Inventory
Isolated State Systems
Vending Machine State/Inventory Checker
Isolated State Systems
Inventory
Machine State
const store = new Vuex.Store({
state: { inventory: [...] machineName: Bender lastServiced: new Date(), } ... })
Inventory
Machine
const store = new Vuex.Store({
state: { inventory: [...] } ... })
const store = new Vuex.Store({
state: { machineName: Bender lastServiced: new Date(), } ... })
const store = new Vuex.Store({ modules: { inventory: { state: { supply: [...] }
},
machine: {
state: { machineName: "Bender" }
},
...
}
})
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
inventory: {
state: {
supply: [...]
},
},
machine: {
state: {
machineName: 'Bender',
lastStocked: '02/25/2019'
},
}
}
});
store.js
<template>
<div>
...
{{ lastServiced }}
<button @click="serviceMachine">
Service
</button>
</div>
</template>
<script>
export default {
name: "VendingMachineAdmin",
computed: {
stock() {
return this.$store.state.inventory.supply
},
machineName() {
return this.$store.state.machine.machineName
},
lastServiced() {
return this.$store.state.machine.lastServiced
}
},
}
</script>
VendingMachineAdmin.vue
state is modularized
Let's move our store, getters, actions and mutations into separate vuex modules
Exercise Time!
https://github.com/shortdiv/vuex-modules
[step-0]
const store = new Vuex.Store({ modules: { inventory: { getters: { isSupplyLow(state) { return state.supply.filter( item => item.supply <= 5 ); } },
}, machine: { actions: { serviceMachine({ commit }) { commit("updateServiceDate", new Date()); } }, }, })
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
inventory: {
getters: {
isSupplyLow(state) {
return state.supply.filter(item => item.supply <= 5);
}
},
},
machine: {
actions: {
serviceMachine({ commit }) {
commit("updateServiceDate", new Date());
}
},
}
}
});
store.js
<template>
<div>
...
{{ lastServiced }}
<button @click="serviceMachine">
Service
</button>
</div>
</template>
<script>
export default {
name: "VendingMachineAdmin",
computed: {
isSupplyLow() {
return this.$store.getters.isSupplyLow
}
},
methods: {
serviceMachine() {
return this.$store.dispatch("serviceMachine")
},
},
}
}
</script>
VendingMachineAdmin.vue
actions, getters, mutations are global
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
inventory: {
getters: {
isSupplyLow(state) {
return state.supply.filter(item => item.supply <= 5);
}
},
},
machine: {
actions: {
serviceMachine({ commit }) {
commit("updateServiceDate", new Date());
}
},
}
}
});
store.js
<template>
<div>
...
{{ lastServiced }}
<button @click="serviceMachine">
Service
</button>
</div>
</template>
<script>
import { mapActions, mapGetters } from "vuex";
export default {
name: "VendingMachineAdmin",
computed: {
...mapGetters(["isSupplyLow"]);
},
methods: {
...mapActions(["serviceMachine"]);
},
}
}
</script>
VendingMachineAdmin.vue
actions, getters, mutations are global
const store = new Vuex.Store({ modules: { inventory: { namespaced: true, state: { stock: [...] } },
machine: { namespaced: true, state: { machineName: 'Bender’, lastServiced: '01/25/2020' } }
} });
Inventory
Machine
<template>
<div>
...
{{ lastServiced }}
<button @click="serviceMachine">
Service
</button>
</div>
</template>
<script>
import { mapActions, mapActions } from "vuex";
export default {
name: "VendingMachineAdmin",
computed: {
stock() {
return this.$store.state.inventory.stock
},
lastServiced() {
return this.$store.state.machine.lastServiced
}
},
methods: {
restock() {
this.$store.dispatch("inventory/fetchFromInventory");
},
serviceMachine() {
this.$store.dispatch("machine/serviceMachine")
}
}
}
</script>
VendingMachineAdmin.vue
Let's namespace our vuex module and update the actions, and getters in the component appropriately
Exercise Time!
https://github.com/shortdiv/vuex-modules
[step-1]
<template>
<div>
...
{{ lastServiced }}
<button @click="serviceMachine">
Service
</button>
</div>
</template>
<script>
import { mapActions, mapActions } from "vuex";
export default {
name: "VendingMachineAdmin",
computed: {
...mapState({
stock: state => state.inventory.stock,
machineName: state => state.machine.machineName,
lastServices: state => state.machine.lastServiced
})
},
methods: {
...mapActions([
'inventory/fetchFromInventory',
'machine/serviceMachine'
])
}
}
</script>
VendingMachineAdmin.vue
...mapState({ a: state => state.module.name.a, b: state => state.module.name.b })
mapState
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
inventory: {
state: {
supply: [...]
},
},
machine: {
state: {
machineName: 'Bender',
lastStocked: '02/25/2019'
},
}
}
});
store.js
<template>
<div>
...
{{ lastServiced }}
<button @click="serviceMachine">
Service
</button>
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
name: "VendingMachineAdmin",
computed: {
...mapState({
stock: state => state.inventory.supply,
machineName: state => state.machine.machineName,
lastServiced: state => state.machine.lastServiced
})
},
}
</script>
VendingMachineAdmin.vue
state is modularized
<template>
<div>
...
{{ lastServiced }}
<button @click="serviceMachine">
Service
</button>
</div>
</template>
<script>
import { mapActions } from "vuex";
export default {
name: "VendingMachineAdmin",
computed: {
...mapState({
stock: state => state.inventory.stock,
machineName: state => state.machine.machineName,
lastServices: state => state.machine.lastServiced
})
},
methods: {
...mapActions([
'inventory/fetchFromInventory',
'machine/serviceMachine'
])
}
}
</script>
VendingMachineAdmin.vue
...mapActions([
'moduleName/foo',
'moduleName/bar'
])
mapActions
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
inventory: {
namespaced: true,
state: {
stock: [...]
},
actions: {
fetchFromInventory() {...}
}
},
machine: {
namespaced: true,
state: {
machineName: 'Bender',
lastStocked: '02/25/2019'
},
actions: {
serviceMachine() {...}
}
}
}
});
store.js
<template>
<div>
...
{{ lastServiced }}
<button @click="serviceMachine">
Service
</button>
</div>
</template>
<script>
import { mapState, mapActions } from "vuex";
export default {
name: "VendingMachineAdmin",
computed: {
...mapState({
stock: state => state.inventory.stock,
machineName: state => state.machine.machineName,
lastServiced: state => state.machine.lastServiced
})
},
methods: {
...mapActions([
'inventory/fetchFromInventory',
'machine/serviceMachine'
])
}
}
}
</script>
VendingMachineAdmin.vue
<template>
<div>
...
{{ lastServiced }}
<button @click="serviceMachine">
Service
</button>
</div>
</template>
<script>
import { createNamespacedHelpers } from 'vuex'
const { mapState, mapActions } = createNamespacedHelpers('inventory')
export default {
name: "VendingMachineAdmin",
computed: {
...mapState({
stock: state => state.stock, // state.inventory.stock
})
},
methods: {
...mapActions([
'fetchFromInventory' // 'inventory/fetchFromInventory'
])
}
}
</script>
VendingMachineAdmin.vue
├── src/ ├── components ├── App.vue ├── store/ ├── module/ └── inventory.js
└── index.js
└── main.js
├── ...
└── public/
module/
inventory.js
Vuex Modules Folder Structure
index.js
const module = {
namespaced: true,
state: { ... },
actions: { ... },
getters: { ... },
mutations: { ... }
};
export default module;
store/modules/inventory.js
import Vue from "vue";
import Vuex from "vuex";
import inventory from "./modules/inventory";
Vue.use(Vuex);
const modules = { inventory };
const store = new Vuex.Store({
modules
});
export default store;
store/index.js
├── src/ ├── components ├── store/ ├── module/ ├── inventory ├── state.js ├── ...
└── index.js
└── index.js
└── public/
module/
inventory/
Vuex Modules Folder Structure
state.js
index.js
Static vs Dynamic Modules
Vending Machine Service
const store = new Vuex.Store({ modules: {
machine: { namespaced: true, state: { machineName: 'Bender’, timesServiced: 0 }, ... }
} });
registered on init
├── src/
├── state/
├── modules/
├── machine.js
├── store.js
├── ...
└── App.vue
├── ...
└── public/
state/
Vuex Folder Structure
modules/
machine.js
store.js
import Vue from "vue";
import Vuex from "vuex";
import machine from "./modules/machine";
Vue.use(Vuex);
const modules = {
machine
};
const store = new Vuex.Store({
modules
});
export default store;
store.js
export default {
namespaced: true,
state() {
return {
timesServiced: 0
};
},
actions: {
serviceMachine({ commit }) {
commit("completeServiceRequest");
}
},
getters: {},
mutations: {
completeServiceRequest(state) {
state.timesServiced++;
}
}
};
./state/modules/machine.js
import Vue from "vue";
import Vuex from "vuex";
import machine from "./modules/machine";
Vue.use(Vuex);
const modules = {
machine
};
const store = new Vuex.Store({
modules
});
export default store;
store.js
export default {
namespaced: true,
state() {
return {
timesServiced: 0
};
},
actions: {
serviceMachine({ commit }) {
commit("completeServiceRequest");
}
},
getters: {},
mutations: {
completeServiceRequest(state) {
state.timesServiced++;
}
}
};
./state/modules/machine.js
export default {
...
created() {
this.registerStoreModule("machine", machineModule);
},
methods: {
registerStoreModule(moduleName, storeModule) {
const store = this.$store;
if (!(store && store.state &&
store.state[moduleName])) {
store.registerModule(moduleName, storeModule);
}
}
}
};
register module in component instance
export default { ... computed: { ...mapState("machine", ["timesServiced"]) }, created() { this.registerStoreModule("machine", machineModule); }, methods: { ...mapActions("machine", ["serviceMachine"]), registerStoreModule(moduleName, storeModule) {
... }
} };
use module as named
https://github.com/shortdiv/vuex-static-vs-dynamic-modules
[step-0]
Exercise Time!
In the `operatorView` component, extrapolate the store and dynamically load it so that servicing the primary machine doesn't update the other machines
(this step may cause a console error, but ignore that for now)
https://github.com/shortdiv/vuex-static-vs-dynamic-modules
[step-1]
Exercise Time!
Similarly, in the `vendingMachineItem` component, extrapolate the store and dynamically load it
import machineModule from "../store/modules/machine";
export default {
name: "VendingMachineItem",
props: {
machine: Object
},
data() {
return {
machineId: null
};
},
computed: {
...mapState({
timesServiced: state => state.machine.timesServiced
})
},
methods: {
...mapActions("machine", ["serviceMachine"]),
registerStoreModule(moduleName, storeModule) {
const store = this.$store;
if (!(store && store.state && store.state[moduleName])) {
store.registerModule(moduleName, storeModule);
}
}
},
created() {
this.registerStoreModule("machine", machineModule);
}
};
module name is duplicated
https://github.com/shortdiv/vuex-static-vs-dynamic-modules
[step-2]
Exercise Time!
In the `vendingMachineItem` component, create a unique id for every module, this way, the module for every item isn't shared
import machineModule from "../store/modules/machine";
export default {
name: "VendingMachineItem",
props: {
machine: Object
},
data() {
return {
machineId: null
};
},
computed: {
timesServiced() {
return this.$store.state[this.machineId].timesServiced;
}
},
methods: {
serviceMachine() {
this.$store.dispatch(`${this.machineId}/serviceMachine`);
},
registerStoreModule(moduleName, storeModule) {
const store = this.$store;
if (!(store && store.state && store.state[moduleName])) {
store.registerModule(moduleName, storeModule);
}
}
},
created() {
this.machineId = this.machine.name.replace(" ", "").toLowerCase();
this.registerStoreModule(`${this.machineId}`, machineModule);
}
};
import machineModule from "../store/modules/machine";
export default {
name: "VendingMachineItem",
props: {
machine: Object
},
data() {
return {
machineId: null
};
},
computed: {
timesServiced() {
return this.$store.state[this.machineId].timesServiced;
}
},
methods: {
serviceMachine() {
this.$store.dispatch(`${this.machineId}/serviceMachine`);
},
registerStoreModule(moduleName, storeModule) {
const store = this.$store;
if (!(store && store.state && store.state[moduleName])) {
store.registerModule(moduleName, storeModule);
}
}
},
created() {
this.machineId = this.machine.name.replace(" ", "").toLowerCase();
this.registerStoreModule(`${this.machineId}`, machineModule);
},
beforeDestroy() {
this.$store.unregisterModule(`${this.machineId}`);
}
};
remember to unregister module on component teardown
Vuex
By shortdiv
Vuex
- 882