当你读完这篇文章我想你也能看懂这张图了!
这篇文章主要是结合官方文档解读下vuex几个常用的概念:
一、安装及配置vuex
二、state
三、Getter
四、Mutation
五、Action
一、安装及配置vuex
首先来安装vuex:
npm install vuex --save
配置vuex
//创建store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
//vuex实例化
const store = new Vuex.Store({
state:{
my_data:"lxc"
},
mutations:{
name(val){
state.my_data = val
}
}
})
export default store //导出配置
把vuex注入到vue根实例中
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store' //引入store.js
Vue.config.productionTip = false
new Vue({
router,
store, //把vuex注册到根实例中 ,这样每个组件都可以使用vuex
render: h => h(App),
}).$mount('#app')
核心store仓库:
每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,之所以这么说,我们打印store实例看下:
const store = new Vuex.Store({
··· ···
});
console.log(store)
vuex与其他单纯的全局对象不同点:
1 · vuex的状态存储是响应式的,当store中的数据发生变化,相对应的组件也会更新。
2 · 更改store中状态(数据)的唯一途径要通过提交(commit)mutations的方式;通俗点说就是:通过mutations中的方法去更改store中的数据。
------------------------------------------------------------------------------------------------
二、state
简单说,state就是用来存储数据的:
const store = new Vuex.Store({
state:{
name:'lxc',
age:20,
address:'YanTai'
},
mutations:{
··· ···
}
});
(1)vue组件中读取数据
1 > this.$store.state.xxx
2 > 使用辅助函数mapState
在vue组件中获取vuex中的状态信息最简单的方法就是在计算属性中,通过this.$store.state.xxx来获取;之所以要在计算属性中获取数据,因为vuex的状态管理存储是响应式的,所以每次数据状态的变化,计算属性都能获取到最新的数据状态:
computed:{
getStoreName(){
return this.$store.state.name
},
getStoreage(){
return this.$store.state.age
},
getStoreAddress(){
return this.$store.state.address
},
}
补充:
现在多数项目,vuex都会使用模块化,使用模块化时,我们在组件中使用如下方式获取:
this.$store.state.模块下的文件.属性
如:下边需要获取module下的user.js文件中的state -> this.$store.user.xxx
mapState辅助函数
实际开发中,如果state中的包含多个数据状态,在计算属性中使用 this.$store.state.xxx 代码会有些重复、冗余!!!比如上边在计算属性中的代码。幸运的是vuex提供给我们一些辅助函数,帮助我们简化代码,mapState函数,返回到是一个对象,可以生成计算属性:
//store.js
const store = new Vuex.Store({
state:{
name:'lxc',
age:20,
address:'YanTai'
},
mutation:{
··· ···
}
});
//在vue组件中:
//引入mapState辅助函数,它返回的是一个对象,下边是使用对象结构赋值的写法
import {mapState} from 'vuex'
export default {
name:'home',
data(){
return{
name:'鸡小西'
}
},
computed:{
otherComputed(){
··· ···
},
mapState({
// 写法一:函数形式,在函数里可以使用this
nameAlias(state){
console.log(state)
return this.name +"+"+ state.name
},
//写法二:对象形式
ageAlias:'age',
addressAlias:'address'
})
},
}
上边代码,mapState返回的是一个对象,所以可以使用扩展运算符的写法,同时也很容易的将mapState混入(加到)计算属性computed中!!!获取store中的数据不再使用this.$store.state.xxx , 而是可以使用对象键值对的形式来获取store中的数据;也可以使用函数形式获取(使用函数优点在于:可以使用局部this,从而使用store中数据更加灵活!),参数state是一个包含在store中的所有数据对象:
补充:
如果vuex使用模块化,在组件中获取state属性:
const { mapState } from 'vuex'
export default {
computed: {
mapState({
count: state => state.模块名.属性
})
}
}
------------------------------------------------------------------------------------------------
三、getter
通俗来说getter相当于store中的计算属性。
官方文档:有时候我们需要从store中派生出一些状态(也就是说store中的数据可以通过加工、过滤、重新计算等过滤出一些数据,供vue组件使用),这时getter上场了;
在store中使用getter最大的优点:
(1)你可以在store中,集中处理数据,然后供给各个组件享用,而不需要在各个组件中分别来处理数据了!!!
(2)getter的返回值会根据它所依赖的数据进行缓存,只有依赖数据发生改变时才会重新计算!!!
const store = new Vuex.Store({
state:{
my_data:[
{id:1,content:"吕星辰",status:true}
]
},
getters:{
filterData:function(state){
console.log(state)
return xxxx
}
}
··· ···
})
上边代码,getters接受state作为第一个参数,与即将讲到的mutations一样,参数state是stroe中的所有数据。。。
获取gtter中的数据
与获取state中的数据相似,有两种方式:
(1) this.$store.getters.xxx
(2) 使用辅助函数mapGetters
//使用this.$store.getters.xxx 获取gtters中的数据
computed:{
getFilterData(){
return this.$store.getters.xxx
}
},
//使用辅助函数mapGetters来获取gtters中的数据
import {mapState,mapGetters} from 'vuex'
··· ···
computed:{
//写法一:使用数组形式
mapGetters([
'filterData' //这个值是定义在getters中的属性
])
//写法二:使用对象键值对形式,为数据起了一个别名
mapGetters({
dataAlias:'filterData' //这个值是定义在getters中的属性
})
},
下边我们来看一个较完整的例子:在store中通过getter过滤掉status为false的数据,组件可以把getter中过滤掉的数据渲染到页面:
//store.js
const store = new Vuex.Store({
state:{
my_data:[
{id:1,content:"吕星辰",status:true},
{id:2,content:"鸡小西",status:true},
{id:3,content:"王二胖",status:false},
{id:4,content:"张大熊",status:true},
]
},
// 通过getter(计算属性)来过滤掉status为false的数据
getters:{
filterData:state=>{
return state.my_data.filter(item=>item.status)
}
},
mutation:{
··· ···
}
});
<!--在组件中-->
<template>
<div>
<p>{{getFilterData}}</p> <!--数据渲染到页面-->
</div>
</template>
<script>
export default {
name:'home',
computed:{
//获取getter中的数据
getFilterData(){
return this.$store.getters.filterData
}
}
}
</script>
渲染结果:
------------------------------------------------------------------------------------------------
四、mutations 英文意思:突变、变化
想必对mutations都不是太陌生,对,没错!修改store状态的唯一方法就是通过提交mutations来实现 !!!
先来看下mutations
Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数。( 官方这句话我理解的意思是:在vue组件中修改store中的数据方法之一,需要使用 this.$store.commit( ' eventTypeName ' ),而这个字符串参数 就是事件类型同时也是回调函数的名子 )
来看下官方文档中的例子,我又补充了一些代码:
//store.js
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
// 变更状态
state.count++
}
}
})
//vue组件中,通过this.$store.commit('xxx')来修改store中的数据
export default {
name:'home',
mounted(){
this.$store.commit('increment')
}
}
触发mutations中的回调函数有两种方式:
(1) this.$store.commit.xxx
在调用$store.commit时,除了参数一事件类型(就是回调函数名字)之外,还可以传第二个参数(官方叫提交载荷Payload),mutations 的回调函数第二个参数就是传过来的载荷Payload,通常情况下载荷Payload是一个对象:
//vue组件中
export default {
name:'home',
mounted(){
//写法一:
this.$store.commit('increment',{countParam:100})
//写法二:对象格式的,type是必须包含的
this.$store.commit({type:'increment',countParam:100})
}
}
//store.js
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state,param) {
// 变更状态
state.count += param.countParam
}
}
})
上边代码,在vue组件中触发mutations中的回调函数,使用了2种方法传参;在vue-devtools中能清楚的看到,数据被修改了:
(2) 使用辅助函数mapMutations
与state、getter 类似,需要引入mapMutations
import {mapState,mapGetters,mapMutations} from 'vuex'
methods:{
mapMutations(
//写法一:使用对象格式
{add:'increment'}
//写法二:使用数组格式
['increment']
)
}
这里我着重说下使用 mapMutations如何传参:
我们都知道使用 this.$store.commit( 'xxx' , params ) 可以传递参数,但是很多人不知道使用mapMutations如何传递参数!!!首先要明白 使用 ...mapMutations(['increment' ]) 里边的字符串参数会被挂载到vue实例上去,实际上它就是一个函数
export default {
name:'home',
mounted(){
console.log(this)
},
methods:{
mapMutations(['increment'])
}
}
所以,如果你想使用mapMutations传递参数,你可以这样写:
export default {
name:'news',
data(){
return{
my_count:100
}
},
//在钩子函数中,调用increment函数,括号中传递参数,这个参数会被mutations中回调函数的第二个参数接收
mounted(){
this.increment(this.my_count)
},
methods:{
mapMutations(['increment'])
}
}
//stroe.js
const store = new Vuex.Store({
state:{
count:1
},
mutations:{
increment (state,param) {
state.count += param
}
}
});
上边代码,在mounted钩子函数中,为inrement函数传递参数,我们在vue-devtools中查看修改结果:
使用mutation需要注意:
(1)最好提前在stroe中初始化好所有的属性。
(2)如果你想在对象上添加新属性或者修改对象时,有种方法:
- 使用Vue.set ( state , 'count' , '123' ) ;三个参数分别表示:在哪个对象中;要添加的属性;属性值是对少。。。
- 以新对象替换老对象 (没看懂官方这个!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!)
(3)mutations中的函数必须是同步函数,原因是非常难调试,具体原因查看官方文档!
------------------------------------------------------------------------------------------------
五、Action
先来看下官方文档:Action类似于mutation,不同的地方在于:
1、Action提交的是mutation(通俗点说就是修改的mutations中的状态),而不是直接修改state中的状态
2、Action可以包含任意异步操作(也就是说:在action中可以异步操作数据)
1、看下边官方案例,注册一个简单的action:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
change(state) {
state.count++
}
},
actions: {
change(context) {
context.commit('change')
}
}
})
上边代码,Action中的函数接收了一个与store实例具有相同方法和属性的context对象( 也就是一个上下文对象,相当于this ),所以可以直接通过context来调用commit ( context.commit ( ) )来触发mutations中的方法。
在实际开发中,我们经常会用到结构赋值来简化代码,特别需要多次调用commit的时候:
const store = new Vuex.Store({
··· ···
actions: {
change({commit}) {
commit('change')
}
}
})
上边代码,参数是用结构赋值的方式,这也是官方推荐的写法,刚开始没太明白为什么会这么写!后来把参数输出才恍然大悟!我们把context打印出来看下,结果是一个对象,所以用结构赋值获取对象的属性要用 { xxx }方式获取
actions:{
change(context){
console.log(context)
}
}
2、触发action
再讲此问题之前,先说明下:为什么非要在 action 中 去触发mutations中的函数去修改数据呢,不如直接操作mutation多好?还记得 mutation 必须同步执行这个限制么?Action 就不受约束!
与前几个属性类似,在组件中触发action有两种方法:
(1) this.$store.dispatch('xxx')
//在组件中
mounted(){
//写法一:
this.$store.dispatch('change',{count:10})
//写法二:
this.$store.dispatch({type:'change',count:10})
},
//store.js
const store = new Vuex.Store({
state:{
count:1
},
mutations:{
increment(state,param){
state.count += param
}
},
actions:{
change(context,countParam){
context.commit('increment',countParam.count)
}
}
});
在vue-devtools中,可看到数据被修改:
(2) 使用辅助函数mapActions
export default {
name:'home',
mounted(){
console.log(this)
this.addCount({count:100})
},
methods:{
//mapActions中使用对象的形式
mapActions(
{addCount:'change'}
)
}
}
值得注意的是:上边代码,mapActions中使用对象格式,对象的key会被挂载到vue实例上去,就是一个函数,所以传递参数的时候应该调用this.addCount
export default {
name:'home',
mounted(){
console.log(this)
this.change({count:100})
},
methods:{
//mapActions中使用数组的形式
mapActions(['change'])
}
}
上边代码,mapActions中使用数组格式,字符串change会被挂载到vue实例上去,它就是一个函数,所以传递参数的时候应该调用this.change
3、Action异步流程(官方叫组合Action)
Action通常时候异步的,那么我们如何知道什么时候结束呢?你要明白的是action的处理函数返回的是一个promise对象,而且可以被store.dispatch( ) 处理,stroe.dispatch仍然返回一个promise对象:
下边使用官方文档的例子,模拟一个异步,在组件中可以监控、处理结果:
//stroe.js
actions: {
change({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve('成功')
}, 1000)
})
}
}
export default {
name:'home',
mounted(){
this.$store.dispatch('change').then((param)=>{
console.log(param) //1秒后输出 "成功"
})
}
}