一、mapState
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
// store.js
/*
vuex的核心管理对象模块:store
*/
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// 状态对象
const state = { // 初始化状态 这里放置的状态可以被多个组件共享
count: 1,
name: 'daming'
}
const mutations = {}
const action = {}
const getters = {}
export default new Vuex.Store({
state, // 状态
mutations, // 包含多个更新state函数的对象
actions, // 包含多个队形事件回调函数的对象
getters // 包含多个getter计算属性函数的对象
})
// main.js
/*
入口JS
*/
import Vue from 'vue'
import App from './App.vue'
import store from './store'
// 创建vm
/* eslint-disable no-new */
new Vue({
el: '#app',
components: {App}, // 映射组件标签
template: '<App/>', // 指定需要渲染到页面的模板
store // 所有的组件对象都多了一个属性:$store
})
在组件中获取vuex状态
虽然将所有的状态放入Vuex,会使状态变化更显式和易调试,但也会使代码变得冗长和不直观。如果有些状态严格属于单个组件,最好还是作为组件的局部状态,比如temp变量,tempcount,tempcount2作为组件的局部状态。
<!-- App.vue -->
<template>
<div id="example">
{{count}}
{{name}}
{{nameAlias}}
</div>
</template>
<script>
import { mapState } from 'vuex' // 引入mapState
export default {
data () {
return {
// 使用 Vuex 并不意味着你需要将所有的状态放入 Vuex。虽然将所有的状态放到 Vuex 会使状态变化更显式和易调试,但也会使代码变得冗长和不直观。
// 如果有些状态严格属于单个组件,最好还是作为组件的局部状态。你应该根据你的应用开发需要进行权衡和确定。
// 下面的temp变量,tempcount,tempcount2作为组件的局部状态
temp: 'hello',
tempcount: 1,
tempcount2: 2
}
},
computed: {
// tempCountPlusTempCount2 这个计算属性并没有涉及到vuex管理的状态
tempCountPlusTempCount2() {
return this.tempcount+this.tempcount2
}
// 由于 Vuex 的状态存储是响应式的,所以可以使用计算属性来获得某个状态
// 当状态改变时,都会重新求取计算属性,并且触发更新相关联的 DOM
// 通过下面的计算属性,就可以在当前组件中访问到count,name,nameAlias等了 在模板中我们通过大括号符号打印出来
// 下面的计算属性涉及到了vuex管理的状态
count () { // 这实际上是ES6中对象的简化写法 完整写法是 count: function { return this.$store.state.count }
return this.$store.state.age
},
name () { // 这实际上是ES6中对象的简化写法 完整写法是 name: function { return this.$store.state.age }
return this.$store.state.age
},
nameAlias () {
return this.$store.state.name
}
countplustempcount: function (state) {
return this.tempcount + this.$store.state.count
},
countplustempcount2 (state) {
return this.tempcount2 + this.$store.state.count
}
// 但有一个问题
// 当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。比如上面的name(),count(),nameAlias(),显得重复,代码冗长
// 为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性,让你少按几次键:
}
}
</script>
但有一个问题,当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。比如上面的name(),count(),nameAlias(),显得重复,代码冗长。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性,让你少按几次键
mapState接收一个对象
mapState函数的可以接受一个对象Object<string | function>。对象中可以包含字符串或函数。mapState()函数的返回结果是一个对象。
<template>
<div id="example">
{{count}}
{{name}}
{{nameAlias}}
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
data () {
return {
temp: 'hello',
tempcount: 1,
tempcount2: 2
}
},
computed: mapState({
count: 'count', // string 映射 this.count 为 store.state.count的值
// 箭头函数可使代码更简练
name: (state) => state.name, // function 映射 this.name 为 store.state.name的值
nameAlias: 'name', // string 映射 this.nameAlias 为 store.state.name的值
countplustempcount: function (state) { // 用普通函数this指向vue实例,但是在箭头函数中this就不是指向vue实例了,所以这里必须用普通哈数
return this.tempcount + state.count
},
countplustempcount2 (state) {
return this.tempcount2 + state.count
}
})
}
</script>
我们继续看上面的mapState函数
computed: mapState({
count: 'count', // string 映射 this.count 为 store.state.count的值
// 箭头函数可使代码更简练
name: (state) => state.name, // function 映射 this.name 为 store.state.name的值
nameAlias: 'name', // string 映射 this.nameAlias 为 store.state.name的值
countplustempcount: function (state) { // 用普通函数this指向vue实例,但是在箭头函数中this就不是指向vue实例了,所以这里必须用普通哈数
return this.tempcount + state.count
},
countplustempcount2 (state) {
return this.tempcount2 + state.count
}
})
- 上面mapState()函数接收了一个对象。
对象的第一个属性是string类型的,count: ‘count’, 这条语句映射出了this.count, 值等于store.state.count的值。
对象的第二个属性是一个箭头函数,name: (state) => state.name,,映射 this.name 为 store.state.name的值。
对象的第三个属性是一个string类型,nameAlias: ‘name’,映射 this.nameAlias 为 store.state.name的值, 和第一个属性的用法本质是一致的,不过这里映射出的计算属性的名称与 state 的子节点名称不同。
对象的第四个属性是一个普通函数,普通函数和箭头函数的不同之处在于,普通函数中的this指向了vue实例,因为可以访问到当前组件的局部状态,比如this.tempcount。
对象的第五个属性是一个普通函数,第五个和第四个的用法本质是一样的,只不过第五个用了ES6中对象的简化写法。
- 上面的mapState函数的返回值是一个对象,我们可以看作是这样的
computed:
{ // 这个对象就是mapState的返回值
count () {
return this.$store.state.count
},
name () {
return this.$store.state.name
}
nameAlias () {
return this.$store.state.name
}
countplustempcount: function (state) {
return this.tempcount + this.$store.state.count
},
countplustempcount2 (state) {
return this.tempcount2 + this.$store.state.count
}
}
把这个对象赋值给computed不就和原始的写法一样了吗,所以mapState起到了简化的作用。但是我们可以发现比起直接给computed赋值,这里少了tempCountPlusTempCount2这个计算属性。tempCountPlusTempCount2不是有vuex状态计算而来的,是根据组件内的局部状态计算来的。
mapState函数结合对象的扩展运算符运算符使用
对象的扩展运算符(…)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。为什么要用扩展运算符呢,我们观察到上面直接将mapState函数的返回值赋给computed对象的话,那么computed中就只有对vuex状态的获取,而没有了当前组件的局部状态,比如tempCountPlusTempCount2就没地方放了,所以我们用扩展运算符。
let z = { a: 3, b: 4 };
let n = { ...z }; // 对象的扩展运算符(...)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中
n // { a: 3, b: 4 }
所以利用扩展运算符将下面mapState函数的返回值对象
computed:{
...mapState({
count: 'count', // string 映射 this.count 为 store.state.count的值
// 箭头函数可使代码更简练
name: (state) => state.name, // function 映射 this.name 为 store.state.name的值
nameAlias: 'name', // string 映射 this.nameAlias 为 store.state.name的值
countplustempcount: function (state) { // 用普通函数this指向vue实例,但是在箭头函数中this就不是指向vue实例了,所以这里必须用普通哈数
return this.tempcount + state.count
},
countplustempcount2 (state) {
return this.tempcount2 + state.count
}
})
}
上面的结果等于
computed:{
count () {
return this.$store.state.count
},
name () {
return this.$store.state.name
}
nameAlias () {
return this.$store.state.name
}
countplustempcount: function (state) {
return this.tempcount + this.$store.state.count
},
countplustempcount2 (state) {
return this.tempcount2 + this.$store.state.count
}
}
于是可以将组将内的计算属性和获取vuex状态的计算属性写在一起了。
computed:{
tempCountPlusTempCount2() {
return this.tempcount+this.tempcount2
},
...mapState({
count: 'count', // string 映射 this.count 为 store.state.count的值
// 箭头函数可使代码更简练
name: (state) => state.name, // function 映射 this.name 为 store.state.name的值
nameAlias: 'name', // string 映射 this.nameAlias 为 store.state.name的值
countplustempcount: function (state) { // 用普通函数this指向vue实例,但是在箭头函数中this就不是指向vue实例了,所以这里必须用普通哈数
return this.tempcount + state.count
},
countplustempcount2 (state) {
return this.tempcount2 + state.count
}
})
}
这就是mapState的基本用法。
mapState函数接受一个数组
当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState 传一个字符串数组。
computed: mapState([
// 映射 this.count 为 store.state.count
'count',
'name'
])
上面的写法可以看作
computed: {
count () {
return this.$store.state.count
},
name () {
return this.$store.state.name
}
}
最终版的mapState
computed: {
tempCountPlusTempCount2() {
return this.tempcount+this.tempcount2
},
...mapState(['count','name']),
...mapState({
nameAlias: 'name', // string 映射 this.nameAlias 为 store.state.name的值
countplustempcount: function (state) { // 用普通函数this指向vue实例,但是在箭头函数中this就不是指向vue实例了,所以这里必须用普通哈数
return this.tempcount + state.count
},
countplustempcount2 (state) {
return this.tempcount2 + state.count
}
})
}
二、mapMutations
mapMutations是vuex的mutation的辅助函数,用于在组件中映射mutation内的方法,以便在该组件中直接使用mutation里的方法 (说白了,就是一语法糖)
1.在组件中导入vuex中的mapMutations:
import { mapMutations } from 'vuex'
2.在组件中导入mutation里的方法名:
...mapMutations([ //使用es6的拓展运算符
'INCREASE_SHOPCART',
'DECREASE_SHOPCART'
])
//约定将mutation里的方法名为大写,并且导入时要给其加上引号
举个栗子:点击btn按钮增减商品数量
//shopCart.vue
//template
<button class="fl" @click='decrease(item.id)'>-</button>
<input type="number" class="fl" v-model="item.count" >
<button class="fl" @click='increase(item.id)'>+</button>
//mutations.js
INCREASE_SHOPCART(state,id){
state.shopCartData.forEach(e=>{
if(e.id === id){
e.count ++
}
})
},
DECREASE_SHOPCART(state,id){
state.shopCartData.forEach(e=>{
if(e.id === id && e.count >1){
e.count --
}
})
}
import { mapMutations } from 'vuex' // 先从vuex里导入 mapMutations
methods:{
...mapMutations([
'INCREASE_SHOPCART', //将mutation里的方法映射到该组件内
'DECREASE_SHOPCART' //等同于this.$store.commit('DECREASE_SHOPCART')
]),
increase(id){
this.INCREASE_SHOPCART(id)
//由于上一步已经将mutation映射到组件内,所以组件可以直接调用INCREASE_SHOPCART
}
decrease(id){
this.DECREASE_SHOPCART(id)
//同理
}
}
三、mapActions
actions功能与mutations相近,区别主要有以下两点:
- actions不能直接改变state,只能通过调用mutation来改变state(mutations不能调用mutations,但是actions可以);
- mutations只能执行同步操作,而actions可以执行异步操作。
如果我们把state、getters、mutations和actions一股脑全部定义在一个文件里,会导致Vuex的定义文件非常臃肿,因此在下面的例子里我们尝试把actions拆出来。首先需要单独定义actions(新增文件路径为src\store\action.js),代码如下:
export default {
// context对象中包含state、commit和dispatch,分别对应Vuex中的state、执行mutations方法和执行actions方法
action1: context => {
setTimeout(() => {
context.commit("mutation1");
}, 1000);
}
};
然后在Vuex中引入并声明actions(修改文件路径为src\store\index.js),代码如下:
import Vue from "vue";
import Vuex from "vuex";
import action from "./action";
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
param1: "state param"
},
getters: {
param2: state => {
return `new ${state.param1}`;
},
// 在getters中可以使用其他getters
param3: (state, getters) => {
return `another ${getters.param2}`;
},
// getter支持返回一个函数
param4: state => index => {
return state.paramArray[index];
}
},
// mutations不支持相互调用(如下面的mutation1不能调用mutation2)
mutations: {
mutation1: state => {
// 在mutations的回调函数内可以修改state的值
state.param1 += " add something";
},
// mutations支持传递参数(第二个参数)
mutation2: (state, addString) => {
state.param1 += addString;
}
},
actions: action
});
export default store;
接下来在组件中引入并调用(新增文件路径为src\components\componentG.vue),代码如下:
<template>
<div>
<span>actions和mapActions用法</span>
<br />
<span>state in vuex:{{param1}}</span>
<br />
<button @click="action1()">mapActions</button>
</div>
</template>
<script>
import { mapState, mapActions } from "vuex";
export default {
name: "component-g",
computed: {
...mapState(["param1"])
},
methods: {
...mapActions(["action1"])
// action1() {
// // 通过this.$store.dispatch("actions方法名")的方式可以调用actions,当然,定义...mapActions(["action1"])然后直接写this.action1();肯定更方便
// this.$store.dispatch("action1");
// }
}
};
</script>
<style scoped>
</style>
eg:登录后调用获取实体图标的接口