在 Vue
中,组件之间共享数据的方式有:
v-bind
属性绑定,例如: :data="products"
v-on
事件绑定,例如:@click="clickMe"
EventBus
事件总线,例如:$emit
用于发送数据的组件,$on
用于接受数据的组件以上三种方案只适合在小范围内实现数据共享,如果我们要频繁的或者说在大范围内实现数据共享,那么就需要用到 vuex
了。
Vuex
是实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间的数据共享。
画图
使用 Vuex
统一管理状态的好处
Vuex
中集中管理共享的数据,易于开发和后期维护Vuex
中的数据都是响应式的,能够实时保持数据与页面的同步,即:若存储在 Vuex
中的数据发生了变化,那么那些引用了全局的数据的组件也能够实时的进行页面刷新一般情况下,只有组件之间共享的数据,才会必要存储到 vuex
中,对于组件中的私有数据,依旧存储在组件自身的 data
中即可。
1、安装依赖包
npm i vuex --save
或
yarn add vuex
2、在 store/index.js
中导入
import Vuex from "vuex";
Vue.use(Vuex);
3、创建store
对象,在 store/index.js
中
export default new Vuex.Store({
// state中存放的就是全局共享的数据
state: { count: 100 },
getters: {},
mutations: {},
actions: {},
modules: {},
});
4、将store
对象挂载到vue
实例中,在 main.js
中
new Vue({
router,
// 将创建的共享数据对象,挂载到Vue实例中,所有的组件,就可以直接从store中获取全局的数据了
store,
render: (h) => h(App),
}).$mount("#app");
上述步骤其实在我们创建项目的时候就已经完成了,这里只需了解即可。
Vuex
中的主要核心概念如下:
在Vuex
中,State
提供唯一的公共数据源,所有共享的数据都要统一放到State
中进行存储,例如这里的Store
相当于一个用于存储数据的公共容器。
export default new Vuex.Store({
state: { count: 100 },
mutations: {},
getters: {},
actions: {},
modules: {},
});
上述代码,我们存储了一个 count
变量,默认值 100
,那么我们该如何访问到这个变量的值呢?
1、组件访问State
中数据的第一种方式
# 格式:
this.$store.state.全局数据名称
# 示例:由于我们是在 html 元素中调用,则可以省略 this。在 HomeView.vue 中
<template>
<div id="home">
{{ $store.state.count }}
</div>
</template>
2、组件访问State
中数据的第二种方式
通过mapState
辅助函数的方式,将当前组件需要的全局数据,映射为computed
计算属性,在 HomeView.vue
中
<template>
<div id="home">
<!--直接在组件中获取属性-->
{{ count }}
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
computed: {
// ...表示展开映射,意思就是将全局属性映射为当前组件的计算属性
...mapState(["count"]),
},
};
</script>
count
的值加 1
1、原始方法,在 HomeView.vue
中,修改代码
<template>
<div id="home">
<h3>{{ $store.state.count }}</h3>
<button @click="incrementCount1">点我+1</button>
</div>
</template>
<script>
export default {
data() {
return {};
},
methods: {
incrementCount1() {
this.$store.state.count++;
},
},
};
</script>
解析:
我们可以看到上述代码确实已经实现了我们想要的功能,但是在
Vuex
中,不允许组件直接去修改store
中的数据,这种代码完全是不合法的,为什么不合法呢?
当全局state
数据发生变化,我们就需要一个一个组件的去找,看是哪个组件修改了state
,这种方式不利于我们后期的维护
如果我们使用mutation
里面的函数来修改state
的话,当发现state
里面的数据修改的有问题,那么我们只需要直接来找mutation
即可
在Vuex
中,只能通过mutation
变更Store
中的数据,不可以直接操作Store
中的数据。虽然通过mutation
的方式来操作数据,虽然繁琐了一点,但是却可以集中监控所有数据的变化。
2、使用 mutation
在 store/index.js
中
export default new Vuex.Store({
state: { count: 100 },
mutations: {
// 1、定义 add 方法用于改变 state 中 count 的属性值
add(state) {
state.count++;
},
},
getters: {},
actions: {},
modules: {},
});
在 HomeView.vue
中,
<template>
<div id="home">
<h3>{{ $store.state.count }}</h3>
<button @click="incrementCount1">点我+1</button>
</div>
</template>
<script>
export default {
data() {
return {};
},
methods: {
incrementCount1() {
// 2、通过 commit 触发 mutations 中的 add 方法
this.$store.commit("add");
},
},
};
</script>
3、使用 mutation
并传参数
在 store/index.js
中
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state: { count: 100 },
mutations: {
add(state) {
state.count++;
},
// 1、定义 addN 方法,里面传两个参数,state代表哪个属性,n代表增加几个
addN(state, n) {
state.count += n;
},
},
getters: {},
actions: {},
modules: {},
});
在 HomeView.vue
中,
<template>
<div id="home">
<h3>{{ $store.state.count }}</h3>
<button @click="incrementCount1">点我+1</button>
<br />
<br />
<button @click="incrementCountN">点我+N</button>
</div>
</template>
<script>
export default {
data() {
return {};
},
methods: {
incrementCount1() {
this.$store.commit("add");
},
incrementCountN() {
// 2、通过 commit 触发 mutations 中的 addN 方法并传参数
this.$store.commit("addN", 5);
},
},
};
</script>
总结:
this.$store.commit()
是触发 mutation
的第一种方式。
接下来我们演示 mutation
的第二种使用方式
在 HomeView.vue
中,
<template>
<div id="home">
<h3>{{ $store.state.count }}</h3>
<button @click="incrementCount1">点我+1</button>
<br />
<br />
<button @click="incrementCountN">点我+N</button>
</div>
</template>
<script>
// 1. 从vuex中按需导入mapMutations函数
import { mapMutations } from "vuex";
export default {
data() {
return {};
},
methods: {
// 2、将add和addN方法映射为methods中的函数,供当前组件使用。
...mapMutations(["add", "addN"]),
incrementCount1() {
this.add();
},
incrementCountN() {
this.addN(5);
},
// 或者直接在标签元素中直接@click="add()"
},
};
</script>
对于 mutations
来说,只能够实现同步操作,不可以执行异步操作。
案例:当点击 点我加1
按钮,实现等待一秒钟,使 count
的值加 1
。
1、在 store/index.js
中
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state: { count: 100 },
mutations: {
add(state) {
// 在 mutations 里面的 add 方法中设置延时器,当 触发 add 方法后,count 属性的值延时一秒改变
setTimeout(() => {
state.count++;
}, 1000);
},
addN(state, n) {
state.count += n;
},
},
actions: {},
getters: {},
modules: {},
});
以上代码测试结果如图
我们可以看出,触发add
函数后,页面上的值改变了,但是state
的值并没有变化。所以我们不能在 mutations
里面执行异步操作。
action
其实类似于 mutation
,都是用来修改 state
中属性值的。不同之处在于:
action
提交的是 mutation
,而不是直接变更状态。action
可以包含任意异步操作。2、修改 store/index.js
中的代码
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state: { count: 100 },
mutations: {
add(state) {
// 在 mutations 里面的 add 方法中设置延时器,当 触发 add 方法后,count 属性的值延时一秒改变
// setTimeout(() => {
// state.count++;
// }, 1000);
state.count++;
},
addN(state, n) {
state.count += n;
},
},
actions: {
// 通过context去调用mutation里面的add函数
addAsync(context) {
setTimeout(() => {
// 在 action 中,不能直接修改state 中的数据,若要修改,必须通过 context.commit 触发某个 mutation
context.commit("add");
}, 1000);
},
// 通过context去调用mutation里面的addN函数,并传入参数n
addNAsync(context, n) {
setTimeout(() => {
context.commit("addN", n);
}, 1000);
},
},
getters: {},
modules: {},
});
3、触发actions的第一种方式,在 HomeView.vue
中,
<template>
<div id="home">
<h3>{{ $store.state.count }}</h3>
<button @click="incrementCount1">点我+1</button>
<br />
<br />
<button @click="incrementCountN">点我+N</button>
</div>
</template>
<script>
export default {
data() {
return {};
},
methods: {
// 异步的让 count 自增 1
incrementCount1() {
// dispatch 函数专门用来触发 action
this.$store.dispatch("addAsync");
},
// 异步的让 count 自增 n
incrementCountN() {
this.$store.dispatch("addNAsync", 5);
},
},
};
</script>
这时,我们看到页面上的值改变了,state
中的值也一并发生变化。
总结:
如果通过异步(读取接口)操作变更数据,必须通过
action
,而不能使用mutation
,但是在action
中还是要通过触发mutation
的方式间接变更数据。
4、触发actions的第二种方式,在 HomeView.vue
中,
<template>
<div id="home">
<h3>{{ $store.state.count }}</h3>
<button @click="incrementCount1">点我+1</button>
<br />
<br />
<button @click="incrementCountN">点我+N</button>
</div>
</template>
<script>
// 1. 从vuex中按需导入mapActions函数
import { mapActions } from "vuex";
export default {
data() {
return {};
},
methods: {
// 2. 将指定的 actions 函数,映射为当前组件的 methods 函数
...mapActions(["addAsync", "addNAsync"]),
incrementCount1() {
this.addAsync();
},
incrementCountN() {
this.addNAsync(5);
},
// 3. 也可以在点击按钮中直接@click="addNAsync"
},
};
</script>