这张图记住!!!!!!!!!!!!!!!!!!!!!
一、state
标准的store目录结构
引入vuex以后,我们需要在state中定义变量,类似于vue中的data,通过state来存放共享的状态
--store
--actions
--mutations
--getters
--mutations-type
--index.js
--state
index.js是总入口文件
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
import state from './state.js'
import actions from './actions.js'
import mutations from './mutations.js'
import getters from './getters.js'
export default new Vuex.Store({
state,
actions,
mutations,
getters
})
在state.js中定义变量
export default {
userInfo: {
userName: 'fufu',
age: 21,
sex: '女'
},
likes: '101'
}
组件使用创建项目初始的就行,App.vue自定义的那两个
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
<router-view />
</div>
</template>
这个不是重点!随便整一个就行
about.vue里的内容
<h1>{{ $store.state.userInfo.userName }}</h1>
运行结果:
显示出了相应的内容,有了vuex,我们不必再考虑组件之间的传值,直接就可以通过$store来获取不同的数据。
但是如果需要vuex中的多个数据的时候,这样写很麻烦,可以直接将它定义在computed中。
props,methods,data和computed的初始化都是在beforeCreated和created之间完成的。
例如:
<template>
<div>
{{ userAge }}
</div>
</template>
<script>
export default {
name: "Home",
computed: {
userAge() {
return this.$store.state.userInfo.age;
},
},
};
</script>
mapState辅助函数
使用this.$store.state虽然可以很方便的将state里面的值融入computed,但是如果要取多个值,就会出现以下的情况
computed:{
userInfo(){
return this.$store.state.userInfo
},
likes(){
return this.$store.state.likes
},
...
}
特别多的话就会很麻烦,而这时候vuex又给我们提供了更简便的方法mapState方法,
该方法就可以自动将需要的state值映射为实例的计算属性
我们可以这样写:
<template>
<div>
{{ userInfo.sex }}
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
name: "Home",
computed: mapState(["userInfo", "likes"]),
};
</script>
这里的computed: mapState(["userInfo", "likes"])等价于:
computed:{
userInfo(){
return this.$store.state.userInfo
},
likes(){
return this.$store.state.likes
},
}
记住:用mapState等这种辅助函数的时候,前面的方法名和获取的属性名是一致的。
还有个起别名的功能,简单说一下
computed: mapState({
myInfo: "userInfo",
myLike: "likes"
}),
如果要自定义一个计算属性怎么办呢?怎么添加?
现在已经写成:computed: mapState(["userInfo", "likes"])这样了
现在就要用到es6的新特性:...扩展符
computed: {
name() {
return '123'
},
...mapState(["userInfo", "likes"])
}
二、getters
getters相当于vue中的计算属性,能拿到state里面最新的值
而且getters允许传参,第一个参数就是state
这样,通过getters进一步处理,得到想要的值,
getter.js
export default {
realAge: (state) => {
return state.userInfo.age + 1
},
realLike: (state) => {
return state.likes + '下棋'
}
}
Home.vue
<template>
<div>{{ realAge }} -- {{ realLike }}</div>
</template>
<script>
export default {
name: "Home",
computed: {
realAge() {
return this.$store.getters.realAge;
},
realLike() {
return this.$store.getters.realLike;
},
},
};
</script>
运行结果:
mapGetters辅助函数
mapGetters函数具有mapState的作用,而且其主要用法也是一样的,也能将getters的属性映射到实例的计算属性
computed: {
...mapGetters(["realAge", "realLike"]),
},
同样也可以取别名,这里不写了,上面有个例子^ ^
会发现其实getters和state实际上差不多,但是最大的区别是getters可以传参,$store.state.userInfo.age只能得到固定的值,但是我们$store.getters.realAge得到的值是加工后的值,所以用gettes可以满足更多的数据需求吧。
当然,如果不需要对数据进行加工,$store.getters = $store.state
三、mutations
代码中定义的时候需要mutations,它类似于vue中的methods,
mutations需要通过commit来调用其里面的方法,它也可以传入参数,第一个参数是state,第二个参数是载荷(payLoad),也就是额外的参数。
我们只能通过mutation去更改state里面的值
mutations.js
export default {
addAge: (state, payLoad) => {
state.userInfo.age += payLoad.number
}
}
Home.vue
<template>
<div>
<div>{{ realAge }} -- {{ realLike }}</div>
<button @click="handleAddAge">增加一岁</button>
</div>
</template>
<script>
import { mapGetters } from "vuex";
export default {
name: "Home",
computed: {
...mapGetters(["realAge", "realLike"]),
},
methods: {
handleAddAge() {
this.$store.commit("addAge", { number: 1 });
},
},
};
</script>
运行出来的效果就是,点击按钮,年龄+1
使用mutation不像之前state、getters一样直接调用,而是要用关键字commit来提交mutation
调用的时候第二个参数最好写成对象形式,这样可以传递更多的信息:
this.$store.commit('mutationName',{
key1:val1,
key2:val2,
key3:val3
})
但是,这样写还是会遇到同样的问题,就是如果需要操作多个数据,就会变的麻烦,这时候我们就需要mapMutations,通过它将方法映射过来
3.1 mapMutations辅助函数
跟mapState、mapGetters一样
不同的是,mapMutations是将所有mutations里面的方法映射为实例methods里面的方法
所以我们可以这样用
methods:{
...mapMutations(['addAge'])
}
mapMutations(['addAge'])这一句就相当于下面的代码
methods:{
addAge(payLoad){
this.$store.commit('addAge',payLoad)
}
}
参数我们可以在调用这个方法的时候写入
<button @click="addAge({ number: 1 })">增加一岁</button>
同样也可以有别名
methods:{
...mapMutations({
handleAddAge:'addAge'
})
}
这时候会有疑问,我为什么要绕一圈,从mutations里面去改state呢?我能不能直接改state呢?
比如这样:
addAge(){
this.$store.state.userInfo.age +=5;
}
复制代码
实际看结果也可以,那我为什么从mutations里面中转一下呢?
原因如下:
- 在mutations中不仅仅能做赋值操作
- Vue.js在mutations中做了类似埋点操作,如果从mutations中操作的话, 能被检测到,可以更方便用调试工具调试,调试工具可以检测到实时变化,而直接改变state中的属性,则无法实时监测
注意:mutations只能写同步方法,不能写异步,比如axios、setTimeout等,这些都不能写,mutations的主要作用就是为了修改state的。
原因类似:如果在mutations中写异步,也能够调成功,但是由于是异步的,不能被调试工具追踪到,所有不推荐这样写,不利于调试,这是官方的约定。
3.2 使用常量替代Mutation事件类型
把原本的方法名称由字符串转变成常量
mutations.js
const ADD_AGE = 'addAge'
export default {
[ADD_AGE](state, payLoad) {
state.userInfo.age += payLoad.number
}
}
为什么这样写?
- 不容易写错,字符串容易写错,而且字符串写错以后不会报错位置,而用常量替代,如果写错,eslint可以提示错误位置
- 当使用action派发mutation时,在action中使用同样的常量,避免手滑写错方法
并不是非得这样做
用常量替代mutations的时候我我们可以新建一个文件(mutation_type.js)专门存储这些常量
mutations_type.js
const ADD_AGE = 'addAge'
export{
ADD_AGE
}
注意这里是export不是export default
mutations.js
import {ADD_AGE} from './mutations-type'
export default {
[ADD_AGE](state, payLoad) {
state.userInfo.age += payLoad.number
}
}
四、actions
action类似于mutation
我们只需要记住一下几点:
- action可以提交mutation,然后mutation去更改state
- action不要直接去操作state,而是去操作mutation
- action包含异步操作,类似于axios请求,可以都放在action中写
- action中的方法默认的就是异步,并且返回promise
为什么?因为这是Vuex规定的
举个例子:
actions.js
import {ADD_AGE} from './mutations-type'
export default{
//定义一个异步获年龄的action
async getUserInfo(context){
//context可以理解为它是整个Store的对象.类似于this.$store,里面包含了state,getter,mutations,actions
const res = await axios.get('/接口url')
//这个时候就用到了 mutation_type.js
context.commit( ADD_AGE,{number: res.number}
)
},
}
也可以写setTimeOut:
actions.js
import {ADD_AGE} from './mutations-type'
export default {
setAge(context) {
setTimeout(() => {
context.commit(ADD_AGE, {number: 1})
}, 2000)
}
}
还可以用解构赋值的方式对context里的属性进行解构
import {ADD_AGE} from './mutations-type'
export default {
setAge({ commit }) {
setTimeout(() => {
commit(ADD_AGE, {number: 1})
}, 2000)
}
}
这样就ok了
Home.vue
<template>
<div>
<div>{{ realAge }} -- {{ realLike }}</div>
<button @click="setAge({ number: 1 })">增加一岁</button>
</div>
</template>
<script>
import { mapGetters } from "vuex";
export default {
name: "Home",
computed: {
...mapGetters(["realAge", "realLike"]),
},
methods: {
setAge() {
this.$store.dispatch("setAge");
},
},
};
</script>
梳理一下流程:
-
页面初始化的时候...省略
-
点击按钮调用
this.setAge()方法,this.setAge()派发(dispatch)一个名为setAge的action -
在
setAge这个action中,执行如下操作:setAge(context) { setTimeout(() => { context.commit(ADD_AGE, {number: 1}) }, 2000) } -
age发生了改变,触发computed重新计算,然后拿到了age最新的值
所以看到这里,应该明白Vuex所谓的单向数据流
界面——>派发action——>action提交mutation——>mutation更改state——>getters返回最新的state值到界面
mapActions跟上面的差不多,不写了。
要提一下的是一个action里也可以dispatch其他action
setAge(context) {
setTimeout(() => {
context.commit(ADD_AGE, {number: 1})
context.dispatch('xxx')
}, 2000)
}
五、总结
- 直接获取state的数据,用this.$store.state.值
- 依赖state得到新的数据,用this.$store.getters.值
- 同步修改state的属性值,就用this.$store.commit('mutation值', payLoad)
- 异步修改state的属性值,就用this.$store.dispatch('action值')
- mapState、mapMutations、mapGetters、mapActions等辅助函数,便于我们处理多个方法和属性