简介
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。
state
vuex中的state可以理解为vue中的data,就是存放静态数据的,官方的说明是状态管理,由于 Vuex 的状态存储是响应式的,从 store 实例中读取状态最简单的方法就是在计算属性中返回某个状态。
/src/store/index.js
1 2 3 4 5 6 7 8
| import Vue from 'vue'; import vuex from 'vuex'; Vue.use(vuex); export default new vuex.Store({ state:{ count:1 } });
|
/index.js
1 2 3 4 5 6
| import store from './store/index.js'; new Vue({ store, });
|
/src/pages/Home.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| <template> <section class="home-area"> <h1>首页</h1> <div class="module-list"> <ul> <li @click="goAnimalList('dog')">狗狗列表</li> <li @click="goAnimalList('cat')">猫咪列表</li> </ul> </div> </section> </template>
<script> export default { data() { return {}; }, methods: { goAnimalList(params){ if(params == 'dog'){ this.$router.push('/dogList'); }else if(params == 'cat'){ this.$router.push('/catList'); } }, }, components: {}, computed: { count () { return this.$store.state.count; } }, created(){ console.log(this.count); } }; </script>
<style lang='scss' scoped> </style>
|
getters
vuex中的getter可以理解为vue中的computed,vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
/src/store/index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import Vue from 'vue'; import vuex from 'vuex'; Vue.use(vuex); export default new vuex.Store({ state:{ count:1 }, getters:{ countAdd100:(state) => { return state.count + 100; } } });
|
/src/pages/Home.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| <template> <section class="home-area"> <h1>首页</h1> <div class="module-list"> <ul> <li @click="goAnimalList('dog')">狗狗列表</li> <li @click="goAnimalList('cat')">猫咪列表</li> </ul> </div> </section> </template>
<script> export default { data() { return {}; }, methods: { goAnimalList(params){ if(params == 'dog'){ this.$router.push('/dogList'); }else if(params == 'cat'){ this.$router.push('/catList'); } }, }, components: {}, computed: { count () { return this.$store.state.count; }, countAdd100(){ return this.$store.getters.countAdd100; } }, created(){ console.log(this.count); console.log(this.countAdd100); } }; </script>
<style lang='scss' scoped> </style>
|
mutations
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数。
/src/store/index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import Vue from 'vue'; import vuex from 'vuex'; Vue.use(vuex); export default new vuex.Store({ state:{ count:1 }, getters:{ countAdd100:(state) => { return state.count + 100; } }, mutations:{ increment(state,n){ state.count += n; } } });
|
/src/pages/Home.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| <template> <section class="home-area"> <h1>首页</h1> <div class="module-list"> <ul> <li @click="goAnimalList('dog')">狗狗列表</li> <li @click="goAnimalList('cat')">猫咪列表</li> </ul> <button @click="$store.commit('increment',10)">点我+10</button> {{ count }} </div> </section> </template>
<script> export default { data() { return {}; }, methods: { goAnimalList(params){ if(params == 'dog'){ this.$router.push('/dogList'); }else if(params == 'cat'){ this.$router.push('/catList'); } }, }, components: {}, computed: { count () { return this.$store.state.count; }, countAdd100(){ return this.$store.getters.countAdd100; } }, created(){ console.log(this.count); console.log(this.countAdd100); } }; </script>
<style lang='scss' scoped> </style>
|
在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读:
/src/store/index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import Vue from 'vue'; import vuex from 'vuex'; Vue.use(vuex); export default new vuex.Store({ state:{ count:1 }, getters:{ countAdd100:(state) => { return state.count + 100; } }, mutations:{ increment(state,payload){ state.count += payload.amount; } } });
|
/src/pages/Home.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| <template> <section class="home-area"> <h1>首页</h1> <div class="module-list"> <ul> <li @click="goAnimalList('dog')">狗狗列表</li> <li @click="goAnimalList('cat')">猫咪列表</li> </ul> <button @click="$store.commit({type: 'increment',amount: 100})">点我+100</button> {{ count }} </div> </section> </template>
<script> export default { data() { return {}; }, methods: { goAnimalList(params){ if(params == 'dog'){ this.$router.push('/dogList'); }else if(params == 'cat'){ this.$router.push('/catList'); } }, }, components: {}, computed: { count () { return this.$store.state.count; }, countAdd100(){ return this.$store.getters.countAdd100; } }, created(){ console.log(this.count); console.log(this.countAdd100); } }; </script>
<style lang='scss' scoped> </style>
|
actions
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作,而mutation只能是同步操作。
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。但 context 对象不是 store 实例本身。
/src/store/index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import Vue from 'vue'; import vuex from 'vuex'; Vue.use(vuex); export default new vuex.Store({ state:{ count:1 }, getters:{ countAdd100:(state) => { return state.count + 100; } }, mutations:{ increment(state,payload){ state.count += payload.amount; } }, actions:{ incrementAsync({commit}){ setTimeout(()=>{ commit({type: 'increment',amount: 100}); },3000); } } });
|
/src/pages/Home.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| <template> <section class="home-area"> <h1>首页</h1> <div class="module-list"> <ul> <li @click="goAnimalList('dog')">狗狗列表</li> <li @click="goAnimalList('cat')">猫咪列表</li> </ul> <button @click="$store.commit({type: 'increment',amount: 100})">点我+100</button> {{ count }} <button @click="incrementAsync()">点我三秒后自增100</button> </div> </section> </template>
<script> export default { data() { return {}; }, methods: { goAnimalList(params){ if(params == 'dog'){ this.$router.push('/dogList'); }else if(params == 'cat'){ this.$router.push('/catList'); } }, incrementAsync(){ this.$store.dispatch('incrementAsync'); } }, components: {}, computed: { count () { return this.$store.state.count; }, countAdd100(){ return this.$store.getters.countAdd100; } }, created(){ console.log(this.count); console.log(this.countAdd100); } }; </script>
<style lang='scss' scoped> </style>
|
逻辑就是先dispatch Actions里的异步方法,异步里面commit Mutations的同步方法,最终改变state中的值。
mapState
之前在要使用vuex中的state,是这样的:
1 2 3 4 5
| computed: { count () { return this.$store.state.count; } },
|
但这种写法过于繁琐,于是可以简化:
1 2 3 4 5 6 7
| import { mapState } from 'vuex'; export default { computed:mapState([ 'count', 'total', ]), }
|
上面的这段代码会转换为:
1 2 3 4 5 6 7 8 9 10 11
| import { mapState } from 'vuex' export default { computed: { count () { return this.$store.state.count }, total() { return this.$store.state.total }, } }
|
这么写简单了很多,但这样就会出现一个问题:如何将它与局部计算属性混合使用呢( es6 的扩展运算符)?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import { mapState } from 'vuex'; export default { computed:{ localComputed(){ return none; }, ...mapState([ 'count', ]), ...mapState({ newTotal:'total' }) }, }
|
mapGetters
mapGetters和mapState用法很像:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import { mapState , mapGetters } from 'vuex'; export default { computed:{ localComputed(){ return none; }, ...mapState([ 'count', ]), ...mapState({ newTotal:'total' }), ...mapGetters([ 'countAdd100' ]) }, }
|
mapMutations
你可以在组件中使用 this.$store.commit(‘xxx’) 提交 mutation,或者使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用(需要在根节点注入 store)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| <template> <section class="home-area"> <h1>首页</h1> <div class="module-list"> <ul> <li @click="goAnimalList('dog')">狗狗列表</li> <li @click="goAnimalList('cat')">猫咪列表</li> </ul> <!-- <button @click="$store.commit({type: 'increment',amount: 100})">点我+100</button> --> <button @click="increment({amount: 1000})">点我+1000</button> {{ count }} <button @click="incrementAsync()">点我三秒后自增100</button> </div> </section> </template>
<script>
import { mapState , mapGetters , mapMutations } from 'vuex'; export default { data() { return {}; }, methods: { goAnimalList(params){ if(params == 'dog'){ this.$router.push('/dogList'); }else if(params == 'cat'){ this.$router.push('/catList'); } }, incrementAsync(){ this.$store.dispatch('incrementAsync'); }, ...mapMutations([ 'increment' ]) }, components: {}, computed:{ localComputed(){ return none; }, ...mapState([ 'count', ]), ...mapState({ newTotal:'total' }), ...mapGetters([ 'countAdd100' ]) }, created(){ console.log(PUBLIC_PROJECTNAME); console.log(PUBLIC_ENV); console.log(this.count); console.log(this.newTotal); console.log(this.countAdd100); } }; </script>
<style lang='scss' scoped> </style>
|
mapActions
你在组件中使用 this.$store.dispatch(‘xxx’) 分发 action,或者使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用(需要先在根节点注入 store):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import { mapState , mapGetters , mapMutations , mapActions } from 'vuex'; export default{ methods: { goAnimalList(params){ if(params == 'dog'){ this.$router.push('/dogList'); }else if(params == 'cat'){ this.$router.push('/catList'); } }, ...mapMutations([ 'increment' ]), ...mapActions([ 'incrementAsync' ]) }, }
|
Module
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割。
官方:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const moduleA = { state: { ... }, mutations: { ... }, actions: { ... }, getters: { ... } }
const moduleB = { state: { ... }, mutations: { ... }, actions: { ... } }
const store = new Vuex.Store({ modules: { a: moduleA, b: moduleB } })
|
/src/store/index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| import Vue from 'vue'; import vuex from 'vuex'; import catVuex from './catModule/catVuex'; Vue.use(vuex); const state = { count:20, total:40, }; const getters = { countAdd100:(state) => { return state.count + 100; } }; const mutations = { increment(state,payload){ state.count += payload.amount; } }; const actions = { incrementAsync({commit}){ setTimeout(()=>{ commit({type: 'increment',amount: 100}); },3000); } }; export default new vuex.Store({ state, getters, mutations, actions, modules:{ catVuex, } });
|
/src/store/catModule/catVuex.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const state = { catModuleState : 1 }; const getters = {}; const actions = {}; const mutations = {}; export default { namespaced: true, state, getters, actions, mutations };
|
使用:
1 2 3 4 5 6 7 8
| import { mapState } from 'vuex'; export default { computed:{ ...mapState('catVuex',[ 'catModuleState', ]) }, }
|
mapGetters,mapMutations 和 mapActions使用方法类似于 mapstate:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import { mapState , mapGetters , mapMutations , mapActions } from 'vuex'; export default { computed:{ ...mapGetters('catVuex',[ 'catModuleGetter' ]), }, methods:{ ...mapMutations('catVuex',[ 'catModuleMutation' ]), ...mapActions('catVuex',[ 'catModuleAction' ]) } }
|