Vue中 常见的组件通信方式可分为三类
- 父子通信
父向子传递数据是通过 props,子向父是通过 events($emit); 通过父链 / 子链也可以通信($parent / $children); ref 也可以访问组件实例; provide / inject; $attrs/$listeners;
- 兄弟通信
Bus; Vuex;
- 跨级通信
Bus; Vuex; provide / inject、 $attrs / $listeners、
Vuex简介
当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:
问题一:多个视图依赖于同一状态。
问题二:来自不同视图的行为需要变更同一状态。
对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。
对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。
因此,我们为什么不把组件的共享状态抽取出来,以一个全局单例模式管理呢?
在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为!
通过定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态间的独立性,我们的代码将会变得更结构化且易维护。
这就是 Vuex 背后的基本思想,借鉴了 Flux、Redux 和 The Elm Architecture。与其他模式不同的是,Vuex 是专门为 Vue.js 设计的状态管理库,以利用 Vue.js 的细粒度数据响应机制来进行高效的状态更新。
1. State
vuex中的数据源,我们需要保存的数据就保存在这里,在页面通过 this.$store.state来获取我们定义的数据;
- store\index.js
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { count:10 } })
- views\about.vue
<template> <div class="about"> <h1>state:{{this.$store.state.count}}</h1> </div> </template>
效果:
2. Getters
Getter 相当于 vue 中的 computed 计算属性,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算;
我们可以通过定义 vuex 的 Getter 来获取,Getters 可以用于监听、state中的值的变化,返回计算后的结果;
这里我们修改 views\about.vue 文件如下:
<template> <div class="about"> <h1>getters:{{this.$store.getters.changeCount}}</h1> <h1>state:{{this.$store.state.count}}</h1> </div> </template>
再修改 store\index.js 文件如下,其中getters中的getStateCount方法接收一个参数state,这个参数就是我们用来保存数据的那个对象;
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { count:10 }, getters:{ changeCount(state){ return state.count + 1 } } })
效果:
3. Mutations
如果需要修改 store 中的值唯一的方法就是提交 mutation 来修改;
我们现在 views\about.vue 文件中添加两个按钮,一个加1,一个减1;
这里我们点击按钮调用 addCount 和 reduceCount,然后在里面直接提交 mutations 中的方法修改值;
<template> <div class="about"> <h1>getters:{{this.$store.getters.changeCount}}</h1> <h1>state:{{this.$store.state.count}}</h1> <button @click="addCount"> + </button> <button @click="reduceCount"> - </button> </div> </template> <script> export default { data(){ return { n:10 } }, methods:{ addCount(){ this.$store.commit('add',this.n) // 传递参数 }, reduceCount(){ this.$store.commit('reduce') } } } </script>
修改 store\index.js 文件,添加 mutations,在 mutations 中定义两个函数,用来对count加1和减1,这里定义的两个方法就是上面commit提交的两个方法如下:
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { count:10 }, getters:{ changeCount(state){ return state.count + 1 } }, mutations: { add(state,n){ state.count = state.count + n }, reduce(state){ state.count = state.count - 1 } } })
效果:
4. Actions
通过 mutations ,我们达到了修改store中状态值的目的;
但是,mutation只能是同步,action 可以包含任意异步操作,在actions中提交mutation再去修改状态值;
接下来我们修改 store\index.js文件:
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { count:10 }, getters:{ changeCount:state => state.count + 1 }, mutations: { add(state,n){ state.count = state.count + n }, reduce(state){ state.count = state.count - 1 } }, actions: { addFun(context,n){ context.commit('add',n) // 传递参数 }, reduceFun(context){ context.commit('reduce') } } })
然后我们去修改views\about.vue文件:
<template> <div class="about"> <h1>getters:{{this.$store.getters.changeCount}}</h1> <h1>state:{{this.$store.state.count}}</h1> <button @click="addCount"> + </button> <button @click="reduceCount"> - </button> </div> </template> <script> export default { data(){ return { n:10 } }, methods:{ addCount(){ this.$store.dispatch('addFun',this.n) // 传递参数 }, reduceCount(){ this.$store.dispatch('reduceFun') } } } </script>
效果:
5. 使用 mapState、mapGetters、mapActions 简化
如果我们不喜欢这种在页面上使用 “this. s t r o e . s t a t e . c o u n t ” 和 “ t h i s . stroe.state.count” 和 “this. stroe.state.count”和“this.store.dispatch(‘funName’)” 这种很长的写法,
那么我们可以使用 mapState、mapGetters、mapActions 就不会这么麻烦了;
我们修改 views\about.vue 文件如下:
<template> <div class="about"> <h1>getters:{{this.$store.getters.changeCount}}</h1> <h1>通过mapGetters获取:{{changeCount}}</h1> <h1>通过mapState获取:{{count}}</h1> <h1>state:{{this.$store.state.count}}</h1> <button @click="addCount"> + </button> <button @click="reduceCount"> - </button> </div> </template> <script> import {mapGetters,mapActions,mapState} from 'vuex' export default { data(){ return { n:10 } }, computed:{ ...mapState({ count:state => state.count }), ...mapGetters([ 'changeCount' ]) }, methods:{ ...mapActions( {reduceCount:'reduceFun'}, ), addCount(){ this.$store.dispatch('addFun',this.n) }, // reduceCount(){ // this.$store.dispatch('reduceFun') // } } } </script>
效果: