0%

【Vue】Vuex

Vuex为Vue的一个状态管理插件,用于管理不同组建的同一状态

一、认识Vuex

1.1 简单介绍

  • Vuex是专门为Vue.js应用程序开发的状态管理工具
  • 状态管理可以简单认为是一组变量,用来存储状态
  • 使用共享的状态情况
    • 例如:用户的登录状态,用户的名称,头像,地理位置等等。
    • 例如:商品的收藏,购物车的物品

二、单页面到多页面状态管理切换

2.1 单页面状态管理

  • 拿一个计数器作为例子来介绍单页面

2.2 多页面状态管理

  • 使用Vuex来管理计算器的counter变量
  • (1)安装Vuex
  • npm install Vuex --save
  • (2)配置Vuex
  • 创建一个新的文件夹store,在里面创建index.js来配置Vuex
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
import Vue from 'vue';
import Vuex from 'vuex';

//1.插件安装
Vue.use(Vuex);

//2.创建Vuex对象
const store = new Vuex.Store({
state: {
counter: 100,
},//保存状态
mutations: {

},
actions: {

},
getters: {

},
modules: {

}
});

//3.导出store
export default store;
  • (3)main.js挂载Vuex的配置
1
2
3
4
5
6
7
...
import store from "./store"; //导入Store

new Vue({
...
store //挂载Store
})
  • (4)使用Vuex管理的状态(变量)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<div id="app">
<h2>{{$store.state.counter}}</h2>
<button @click="$store.state.counter++">+</button> <!--此方法修改状态,官方并不推荐-->
<button @click="$store.state.counter--">-</button> <!--因为当多个页面修改此变量时,无法追踪是哪个页面进行修改-->
</div>
</template>

<script>
...
</script>

<style>
...
</style>

三、Vuex的状态修改

  • 官方推荐通过修改actions来操作mutations,再通过mutations来修改state
  • 当然,官方也推荐直接操作mutations,actions操作只是支持异步操作,mutations是同步操作
  • 为了追踪状态的修改,官方提供浏览器插件devtools来进行追踪

3.1 devtools安装和使用

  • (1)安装
  • (2)使用

3.2 使用mutations修改状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ./store/index.js

...
const store = new Vuex.Store({
state: {
counter: 100,
},//保存状态
mutations: {
//方法
increment(state) {
state.counter++;
},
decrement(state) {
state.counter--;
}
}
});
...
1
2
3
4
5
6
7
8
9
<!--使用mutations方法-->
<template>
<div id="app">
<h2>{{$store.state.counter}}</h2>
<button @click="$store.commit('increment')">+</button>
<button @click="$store.commit('decrement')">-</button>
</div>
</template>
...

四、Vuex的核心概念

4.1 State

  • 单一状态树:单一数据源,即无论表示状态的数据有多少,都放在同一个store对象中,不允许创建新的store来分类存放其他状态

4.2 Getters

  • getters使用方法类似于Vue的计算属性,对数据进行一定操作后返回

  • (1)基本使用

1
2
3
4
5
6
7
8
9
10
11
12
//Vuex配置文件
const store = new Vuex.Store({
...
state: {
counter: 100,
},
getters: {
x2Counter(state) {
return state.counter * 2;
}
}
});
1
2
3
4
5
6
<!--页面使用-->
<template>
<div id="app">
<h2>{{"x2:" + $store.getters.x2Counter}}</h2>
</div>
</template>
  • (2)进阶使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//Vue配置文件
const store = new Vuex.Store({
state: {
counter: 100,
}
getters: {
x2Counter(state) {
return state.counter * 2;
},

x4Counter(state, getters){ //支持两个参数,参数2可以为getter,直接调用属性
return getters.x2Counter * 2;
},

xNCounter(state){ //支持返回函数
return function (n) {
return state.counter * n;
}
// return n => state.counter * n; 箭头函数
}
}
});
1
2
3
4
5
6
7
<!--页面使用-->
<template>
<div id="app">
<h2>{{"x4:" + $store.getters.x4Counter}}</h2>
<h2>{{"xN:" + $store.getters.xNCounter(8)}}</h2> <!--相当于 8 => state.counter * 8-->
</div>
</template>

4.3 Mutation

  • (1)mutation传递参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//修改Vuex配置文件
...
const store = new Vuex.Store({
state: {
counter: 100,
},
mutations: {
customIncrement(state, n){ //mutation可以接收参数
state.counter += n;
}
}
...
});
...
1
2
3
4
5
6
7
<!--页面使用-->
<template>
<div id="app">
<h2>{{$store.state.counter}}</h2>
<button @click="$store.commit('customIncrement', 5)">+5</button> <!--传递的参数为payload,称为负载-->
</div>
</template>
  • (2)mutation另一种提交风格
1
2
3
4
5
6
7
8
9
10
11
12
13
...
const store = new Vuex.Store({
state: {
counter: 100,
},
mutations: {
customIncrement2(state, payload){
state.counter += payload.n;
}
}
...
});
...
1
2
3
4
5
6
<template>
<div id="app">
<h2>{{$store.state.counter}}</h2>
<button @click="$store.commit({type: 'customIncrement2', n: 10})">+10</button> <!--另一种提交风格,直接提交一个对象-->
</div>
</template>
  • (3)mutation响应规则

  • (4)mutation的类型常量

    • 为了组件中使用mutation方法时不写错,可以定义一个常量来保存方法名,然后倒入此常量进行调用
    • 类似Java的枚举类
1
2
3
4
//新建一个mutation-type.js文件来定义常量
export const INCREMENT = "increment";
export const DECREMENT = "decrement";
export const CUSTOMINCREMENT = "customIncrement";
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 导入mutation-types.js文件,使用常量 -->
<script>
...
import {INCREMENT, DECREMENT, CUSTOMINCREMENT} from "./store/mutation-types";

export default {
...
methods: {
increment() {
// this.$store.commit('increment');
this.$store.commit(INCREMENT); //直接使用常量
}
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//为了统一使用常量来替换,修改./store/index.js
import {INCREMENT, DECREMENT, CUSTOMINCREMENT} from "./store/mutation-types";
...

const store = new Vuex.Store({
...
mutations: {
/*
increment(state) {
state.counter++;
}
*/
[INCREMENT](state) {
state.counter++;
}
}
});

4.4 Actions

  • actions是类似mutation,但是用来代替mutation来实现异步操作

  • (1)基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//在Vuex配置文件中配置actions
const store = new Vuex.Store({
...
mutations: {
updateInfo(state){
this.state.info.name = "C酱";
}
},

actions: {
aUpdateInfo(context) { //参数不是state,是context,大概理解context为store对象
setTimeout(() => {
context.commit('updateInfo'); //异步操作中修改数据要使用mutation进行修改
}, 1000)
}
}
});
1
2
3
4
5
6
7
8
9
10
11
12
<!--页面使用actions方法-->
<script>
...
export default {
...
methods: {
updateInfo() {
this.$store.dispatch('aUpdateInfo'); //使用actions方法,用dispatch
}
}
}
</script>
  • (2)支持参数传递
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const store = new Vuex.Store({
...
mutations: {
updateInfo(state){
this.state.info.name = "C酱";
}
},

actions: {
aUpdateInfo2(context, payload){ //支持带参
setTimeout(() => {
context.commit('updateInfo');
console.log(payload);
}, 1000)
}
}
});
1
2
3
4
5
6
7
8
9
10
11
<script>
...
export default {
...
methods: {
updateInfo2() {
this.$store.dispatch('aUpdateInfo2', "我是payLoad!"); //参数传递类似于mutation
}
}
}
</script>
  • (3)进阶使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const store = new Vuex.Store({
...
mutations: {
updateInfo(state){
this.state.info.name = "C酱";
}
},

actions: {
aUpdateInfo3(context, payload){ //后台处理数据,完成后前台打印信息提示已完成
//方法一:可以playload传递函数,数据处理完成时,回调函数,前台会打印信息

//方法二:actions支持返回值,可以返回一个Promise对象
setTimeout(() => {
context.commit('updateInfo');
return new Promise(resolve => {
console.log(payload);
}) //可以在这里直接.then(),也可以去前台使用then()
})
}
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
...
export default {
...
methods: {
updateInfo3() {
this.$store.dispatch('aUpdateInfo3', "我是前台消息").then(() => {
console.log("前台打印:处理已完成!")
})
}
}
}
</script>

4.5 Modules

  • 因为Vuex要求使用单一状态树,会导致state会变得很臃肿,所以可以在modules中设置store对象,进行进一步细分
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
//在Vuex的配置文件中配置
const a = {
state: {
name: "C酱"
},
mutations: {
changeName(state) {
state.name = "莱特雷";
}
},
actions: {
aChangeName(context) {
setTimeout(() => {
context.commit('changeName');
}, 1000)
}
},
getters: {
fullName(state) {
return "秃头" + state.name;
},
connect(state, getters, rootState){ //在modules中的getters支持三个参数
return getters.fullName + " plus " + rootState.info.name;
}
}
}


//2.创建Vuex对象
const store = new Vuex.Store({
...
modules: {
a
}
});
1
2
3
4
5
6
7
8
9
10
11
<!--在页面中使用-->
<template>
<div id="app">
<!-- Modules使用-->
<h2>----- Modules使用 -----</h2>
<h3>{{$store.state.a.name}}</h3>
<button @click="$store.commit('changeName')">mutations使用</button>
<button @click="$store.dispatch('aChangeName')">actions使用</button>
<h3>{{$store.getters.connect + '(getters使用)'}}</h3>
</div>
</template>

五、Vuex的目录结构

将Vuex中的参数提取出来,放在单独的文件中,方便管理


六、ES6语法补充:解构

6.1 对象解构

  • 将一个对象里面的变量提取出来,作为一个新的变量
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    //ES6语法补充:对象解构
    const obj = {
    name: "莱特雷",
    age: 18,
    sex: "男"
    }

    //普通方法
    /*
    const name = obj.name;
    const age = obj.age;
    const sex = obj.sex;
    */

    //ES6语法
    const {name, age, sex} = obj; //里面变量顺序可随意,赋值按照变量名并不是顺序

6.2 数组解构

  • 将数组中的元素提取出来,作为一个单独的新变量
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //ES6语法补充:数组解构
    const names = ["莱特雷", "C酱", "咕料"];

    //普通方法
    /*
    const name1 = names[0];
    const name2 = name[1];
    const name3 = name[2];
    */

    //ES6语法
    const [name1, name2, name3] = names;