介绍
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。官方文档。

注意: 并不所项目都需要Vuex,只有vue没有办法解决情况下或者是存储一些被认为是Vue应用中全局变量数据域时你才需要使用Vuex。滥用Vuex会导致全局数据污染不利于维护等问题。
VueX配置
1 2 3 4 5 6 7 8 9 10 11 12
|
import Vue from 'vue' import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({ state: { value: '存放在vuex store中的全局状态' } })
|
1 2 3 4 5 6 7 8 9 10 11 12
|
import Vue from 'vue' import store from './store' import App from './App.vue'
Vue.config.productionTip = false
new Vue({ store, render: h => h(App), }).$mount('#app')
|
Vuex的核心概念
State
Getters
Mutations
Actions
Modules
温馨提示
1 2
| // 使用辅助函数前必须导入 import { mapState , mapGetters, mapMutations, mapActions } from 'vuex';
|
State
概念:是Vuex用来存储数据
的地方,存储在state中数据可以看做是当前应用的全局变量可以在当前应用的任何地方访问。
1 2 3 4 5 6 7
|
export default new Vuex.Store({ state: { book: 'HTML+CSS', } })
|
语法:
- 在vue的任何组件中都可以使用this.$store.state.属性名访问
1 2 3 4 5 6 7 8 9 10 11
| let store = new Vuex.Store({ state: { value: '存放在vuex store中的全局状态value' } })
let vm = new Vue({ el: '#app', store }) vm.$store.state.value
|
- 在开发中我们推荐将store中的state赋值给需要使用该状态的组件的计算属性中(一定不能把state赋值给data,state发生改变时不会重新给data赋值)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| export default { data() { return { myBook: this.$store.state.book } }, computed: { book() { return this.$store.state.book } } }
|
- Vuex 为了简化 state与计算属性配合使用时的代码,提供了一个辅助函数
mapState
可以简化上面的写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <template> <div>{{book}}</div> </template>
<script>
import {mapState} from 'vuex'
export default {
computed: { ...mapState(['book']) } } </script>
|
Getters
概念: getter就是Vuex的计算属性,开发人员可以将state 或其他getter 计算后的的返回值存放在指定getter中,当前getter会将这些依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算
在任何组件中都可以通过this.$store.getter.属性名访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
export default new Vuex.Store({ state: { score: [30, 20, 80, 10, 9, 66], }, getters: { calcScore(state) { let ps = state.score.filter(v => { return v >= 30; }) return ps.join('-'); } }, })
|
语法: getter会接收一个函数作为参数,该函内部有多个参数,并返回当前getter的值
参数一 state 所有的state
参数二 getters 所有getter
- getter在组件中依然存放在组建的计算属性中
1 2 3 4 5 6 7 8 9 10 11 12 13
| <template> <div>{{score}}</div> </template>
<script> export default { computed: { score() { return this.$store.getters.calcScore; } } } </script>
|
- Vuex为 getters 同样提供了辅助函数
mapGetters
- 方法一: mapState可以接收一个字符串数组作为参数,数组中的每一项字符串都会成为当前组件的计算属性并且与Vuex中的同名getter建立映射对应关系。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <template> <div>{{calcScore}}</div> </template>
<script>
import {mapGetters} from 'vuex'
export default {
computed: { ...mapGetters(["calcScore"]) } } </script>
|
- 方法二: mapGetters可以接收对象作为参数,对象的每一个key都会成为当前组件的计算属性名,value必须是一个字符串并且与Vuex中的同名getter建立映射对应关系。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <template> <div>{{s1}}</div> </template>
<script> import {mapGetters} from 'vuex' export default { computed: { ...mapGetters({ s1: 'calcScore' }) } } </script>
|
Mutations
概念:vuex规定mutation是唯一可以修改state的地方
1 2 3 4 5 6 7 8 9 10 11
| export default new Vuex.Store({ state: { book: 'HTML+CSS', }, mutations: { changeBook(state, book) { state.book = book; }, }, })
|
语法:在vuex中通过配置选项mutations创建并使用 $store.commit方法提交mutation
1、在组件中必须使用$store.commit方法提交指定mutation,指定mutation才会触发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <template> <div> <button @click="changeBookByCom">点我更换书籍(mutations)</button> </div> </template> <script> export default { methods: { changeBookByCommit() { this.$store.commit("changeBook", "JavaScript"); } } } </script
|
注意: commit只能接受两个参数,如果你想要的传递多个参数时,请将载荷作为对象提交
1 2 3
| store.commit 提交mutation的唯一方法,该方法至多接受两个参数: 参数一 mutation type <String> 必传 需要触发的mutation函数名 参数二 payload <Any> 可选 提交给mutation的参数
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import Vue from 'vue' import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({ state: { count: 1, arr: ['a', 'b', 'c'], }, mutations: { addCurrentCount(state, num) { state.count += num }, setArr(state, {index, value}) { Vue.set(state.arr, index , value) } } })
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <template> <div> <button @click="setStateCount">add count</button> <button @click="setStateArr">setStateArr</button> </div> </template> <script> export default { methods: { setStateCount() { this.$store.commit('addCurrentCount', 7) }, setStateArr() { this.$store.commit('setArr', {index:0, value: 7}) } } } </script>
|
注意: Mutation 需遵守 Vue 的响应规则 Vuex 的 store 中的状态是响应式的,那么当我们变更状态时,监视状态的 Vue 组件也会自动更新。这也意味着 Vuex 中的 mutation 也需要与使用 Vue 一样遵守一些注意事项:
1、最好提前在你的 store 中初始化好所有所需state属性。
2、当需要在对象上添加新属性时,你应该使用 Vue.set(obj, ‘newProp’, 123), 或者以新对象替换老对象
Vue.set(obj, ‘newProp’, 123)
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
| import Vue from 'vue' import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({ state: { count: 1, arr: ['a', 'b', 'c'], obj: { name: '小华', age: 15 } }, mutations: { addCount(state) { state.count ++ }, setArr(state) { Vue.set(state.arr, 0 , 'A') }, setObj(state) { Vue.set(state.obj,'address', 'gz' ) } } })
|
新对象替换老对象
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
| import Vue from 'vue' import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({ state: { arr: ['a', 'b', 'c'], obj: { name: '小华', age: 15 } }, mutations: { addCount(state) { state.count ++ }, setArr(state) { state.arr = state.arr.map((item,index) => { if(index === 0) { return 'AA' } return item }) }, setObj(state) { state.obj = { ...state.obj, address: '广州' } } } })
|
注意: Mutation 内部只允许同步操作,原因在vuex中mutation是唯一可以修改state的地方,所以开发中我们有时会对mutation进行数据追踪观察state是否按照预期发生改变,这样做的化就可以在开发中捕获大量的错误,如果mutation允许异步操作的话,就会导致我们的数据追踪变得混乱不可查阅。
注意:因为没次提交mutation都是穿入commit函数一个同名mutation字符串,这样的操作有时会导致提交字符串与mutation名不一致导致指定mutation没有被提交,这个问题尤其会出现在多人开发中。为了避免该问题vuex推荐专门创建一个名为 mutation-types.js 文件存储当前应用中所有的mutation名,开发人员就可以使用变量的方式声明提交指定的mutation了
语法:
1 2 3 4 5 6 7 8
|
export const ADD_CURRENT_COUNT = 'ADD_CURRENT_COUNT'
export const SET_ARR = 'SET_ARR'
export const SET_OBJ = 'SET_OBJ'
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import Vue from 'vue' import Vuex from 'vuex' import * as types from './mutation-types'
Vue.use(Vuex)
export default new Vuex.Store({ mutations: { [types.ADD_CURRENT_COUNT](state, num) { }, [types.SET_ARR](state, {index, value}) { }, [types.SET_OBJ](state) { } } })
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <script> import {ADD_CURRENT_COUNT, SET_ARR, SET_OBJ} from '../store/mutation-types'
export default { methods: { setStateCount() { this.$store.commit(ADD_CURRENT_COUNT, 7) }, setStateArr() { this.$store.commit(SET_ARR) }, setStateObj() { this.$store.commit(SET_OBJ) } } } </script>
|
2、Vuex为 mutations 同样提供了辅助函数 mapMutations
mapMutations生成的方法只接受一个参数,这个参数就是当前mutation的载荷。
方法一: mapMutations可以接收一个字符串数组作为参数,数组中的每一项字符串都会成为当前组件的方法并且与Vuex中的同名mutation建立映射对应关系。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <template> <div> // 提交载荷 <button @click="ADD_CURRENT_COUNT(3)">add count</button> // 不提交载荷 <button @click="SET_ARR">set arr</button> </div> </template> <script> import {mapMutations} from 'vuex' import {ADD_CURRENT_COUNT, SET_ARR} from '../store/mutation-types'
export default { methods: { ...mapMutations([ ADD_CURRENT_COUNT, SET_ARR ]), } } </script>
|
方法二: mapMutations可以接收对象作为参数,对象的每一个key都会成为当前组件的方法名,value必须是mutation的同名字符串
与Vuex中的mutation建立映射对应关系。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <template> <div> // 提交载荷 <button @click="addCount(3)">add count</button> // 不提交载荷 <button @click="setArr">set arr</button> </div> </template> <script> import {mapMutations} from 'vuex' import {ADD_CURRENT_COUNT, SET_ARR} from '../store/mutation-types'
export default { methods: { ...mapMutations({ addCount: ADD_CURRENT_COUNT, setArr: SET_ARR }), } } </script>
|
Actions
概念:Vuex给开发人员提供了一个可以执行异步操作的函数action
注意: action函数中接收两个参数
参数一 context 与 store对象相似所以可以访问 context.state / context.getters / context.commit / context.dispatch
参数二 action的载荷,action载荷与mutation一样只有一个如果需要传递多个参数请传递对象
★★★ action是不允许直接修改state的
语法:在Vuex中通过配置选项actions创建,并使用 $store.dispatch方法分发action
1、在应用中通过store.dispatch(‘action名’,载荷)的形式调用
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' import * as types from './mutation-types'
Vue.use(Vuex)
export default new Vuex.Store({ state: { result: {} }, mutations: { [types.SET_RESULT](state, result) { state.result = result } }, actions: { searchMusic(context, keywords) { console.log(context) fetch('http://musicapi.leanapp.cn/search?keywords=' + keywords) .then(res => res.json()) .then(({result}) => context.commit(types.SET_RESULT, result)) } } })
|
1 2 3 4 5 6 7
| <script> export default { created() { this.$store.dispatch('searchMusic', '海阔天空') } }; </script>
|
2、Vuex为 action 同样提供了辅助函数 mapActions
mapActions生成的方法只接受一个参数,这个参数就是当前action的载荷。
方法一: mapActions可以接收一个字符串数组作为参数,数组中的每一项字符串都会成为当前组件的方法并且与Vuex中的同名action建立映射对应关系。
1 2 3 4 5 6 7 8 9 10 11
| <script> import {mapActions} from 'vuex'
export default { created() { this.searchMusic('海阔天空') }, methods: mapActions(['searchMusic']), }; </script>
|
方法二: mapAction可以接收对象作为参数,对象的每一个key都会成为当前组件的方法名,value必须是action的同名字符串
与Vuex中的action建立映射对应关系。
1 2 3 4 5 6 7 8 9 10 11
| <script> import {mapState, mapActions} from 'vuex'
export default { created() { this.search('海阔天空') }, methods: mapActions({search: 'searchMusic'}), }; </script>
|
Modules
由于篇幅较长,Vuex模块化的使用写在了下一篇文章中。
总结
应用层级的状态都应该集中在store中
提交 mutation 是更改状态state的唯一方式,并且这个过程是同步的。
异步的操作应该都放在action里面
CodeSandbox
CodeSandbox在线代码演示
注意 如遇到CodeSandBox打开失败,请尝试按下图操作,然后分窗口就能一边看代码一边看效果啦
