Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
简单说:VueX是个仓库,可以按照一定的规则实现所有组件的数据共享(获取、修改数据),比我们书写的EventBus更加强大。
Vue2 对应的版本:Vuex3
Vue3对应的版本:Vuex4,但实际上Vue3使用的Pinia代替Vuex。
Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。这需要对短期和长期效益进行权衡。
如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。
简单说:开发大型项目使用Vuex,小型项目EventBus就够用了。
npm install vuex@next --save
建议在使用vue/cli 脚手架创建项目的时候,直接将vuex选择上,避免自己按照和配置的麻烦。如果使用vite搭建项目,默认是Pinia,需要手动安装vuex。
每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:
简单说:Vuex一个仓库,保存项目中所有组件的共享数据,他不仅仅全局对象,共享的数据具备响应式,需要按照指定的规则修改共享的数据
vuex有个缺点:在刷新页面时,Vuex中的数据会丢失,这是由于刷新会重新加载应用程序,导致Vuex状态的重置。一般在APP端可以禁用页面的刷新功能,PC端无法禁用。
针对PC端解决方案:
1、使用本地存储
2、配合后台程序实现持久化保存
3、使用专门的插件或库来管理应用程序的状态持久化
vuex有5个核心:
在
src
下创建store文件夹
,其中创建index.js文件
,编写vuex的基本配置和初始化操作
// 导入vuex
import { createStore } from "vuex";
// 创建一个新的 store 实例
const store = createStore({
// 初始化state函数,其中保存的是所有组件共享的数据
state() {
return {
count: 100,
};
},
// 提供修改state中数据的方法
mutations: {
add(state) {
state.count++;
},
},
});
// 导出
export default store;
在
main.js
文件中挂载vuex
import "./assets/main.css";
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
const app = createApp(App);
app.use(router);
// 将 store 实例作为插件安装
app.use(store);
app.mount("#app");
在 views文件夹
下 创建 Hello.vue文件
,获取Vuex中的数据
<template>
<div>
<p>msg:{{ msg }}p>
<p>vuex中的count:{{ count }}p>
div>
template>
<script>
export default {
data() {
return {
msg: "演示vuex的使用",
count: 0,
};
},
// 在vue实例初始化完成后,获取vux中的数据
created() {
// 由于已经将Vuex的实例绑定在app组件上,可以通过this直接获取
// this.$store 即可获取到vuex的实例
console.log(this.$store.state.count);
this.count = this.$store.state.count;
},
};
script>
<style scoped>style>
在 views文件夹
下 创建 world.vue
文件,调用vuex的 mutations
中提供的 add
方法完成数据修改操作
<template>
<div>
<button @click="changeData">修改vuex中的数据button>
div>
template>
<script>
export default {
methods: {
changeData() {
this.$store.commit("add");
// 其实直接修改也是可以的,但是不推荐这个操作
this.$store.state.count = 200;
},
},
};
script>
<style scoped>style>
再次强调,我们通过提交 mutation 的方式,而非直接改变 store.state.count
,是因为我们想要更明确地追踪到状态的变化。这个简单的约定能够让你的意图更加明显,这样你在阅读代码的时候能更容易地解读应用内部的状态改变。此外,这样也让我们有机会去实现一些能记录每次状态改变,保存状态快照的调试工具。有了它,我们甚至可以实现如时间穿梭般的调试体验。
由于 store 中的状态是响应式的,在组件中调用 store 中的状态简单到仅需要在计算属性中返回即可。触发变化也仅仅是在组件的 methods 中提交 mutation。
简单说:通过mutation 中的方法,可以实现共享数据的可控性
Vuex有5个核心概念:state、getters、mutations、actions、modules
小小提示:操作vuex的仓库时,基本都提供两种方式:直接操作和辅助函数
作用: 多个组件需要共享的数据, 入库, 放在state里面.
组件中如何获取到state的数据呢?
// 导入vuex
import { createStore } from "vuex";
// 创建一个新的 store 实例
const store = createStore({
// 初始化state函数,其中保存的是所有组件共享的数据
state() {
return {
count: 100,
user: {
name: "张无忌",
age: 23,
sex: "男",
addr: "冰火岛",
},
};
},
// 提供修改state中数据的方法
mutations: {
add(state) {
state.count++;
},
},
});
// 导出
export default store;
在需要使用vuex中共享数据的页面,使用
this.$store.state.共享数据key值
即可直接获取共享数据
<template>
<div>
<p>{{ count }}p>
<p>{{ user }}p>
div>
template>
<script>
export default {
// 在vue实例初始化完成后,获取vux中的数据
created() {
console.log(this.$store);
console.log(this.$store.state.count);
console.log(this.$store.state.user);
},
// 通过计算属性,返回数据
computed: {
count() {
return this.$store.state.count;
},
user() {
return this.$store.state.user;
},
},
};
script>
<style scoped>style>
mapState
辅助函数vuex提供mapState
辅助函数,更加快捷,灵活的获取vuex中共享的数据,辅助函数有两种书写方式
<template>
<div>
<p>{{ count }}p>
<p>{{ user }}p>
div>
template>
<script>
// 在单独构建的版本中辅助函数为 Vuex.mapState
import { mapState } from "vuex";
export default {
// 通过计算属性,返回数据
computed: {
// 对象方式
// ...mapState({
// count: (state) => state.count,
// user: (state) => state.user,
// }),
// 数组方式
...mapState(["count", "user"]),
},
};
script>
<style scoped>style>
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。
Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的事件类型 (type)和一个回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数。
// 导入vuex
import { createStore } from "vuex";
// 创建一个新的 store 实例
const store = createStore({
// 初始化state函数,其中保存的是所有组件共享的数据
state() {
return {
count: 100,
user: {
name: "张无忌",
age: 23,
sex: "男",
addr: "冰火岛",
},
};
},
// 提供修改state中数据的方法
mutations: {
// state,就是仓库对象
// payload修改的值
// 一般为了区分,会将这些方法名全部大写
CHANGE_COUNT(state, payload) {
console.log("在仓库中,CHANGE_COUNT执行,", state, payload);
state.count += payload;
},
CHANGE_USER(state, payload) {
state.user.age += payload.num;
},
},
});
// 导出
export default store;
使用commit来完成直接修改,this.$store.commit(“函数名”,数据)
<template>
<div>
<p>{{ count }}p>
<p>{{ user }}p>
<button @click="updateCount">修改countbutton>
div>
template>
<script>
// 在单独构建的版本中辅助函数为 Vuex.mapState
import { mapState, mapMutations } from "vuex";
export default {
// 通过计算属性,返回数据
computed: {
...mapState(["count", "user"]),
},
methods: {
updateCount() {
// 直接调用方法修改
// this.$store.commit("CHANGE_COUNT", 3);
// 采用对象方式修改
this.$store.commit({
type: "CHANGE_COUNT",
num: 3,
});
},
},
};
script>
<style scoped>style>
若采用对象方式修改,payload接收到的是一个对象,需要处理。
CHANGE_COUNT(state, payload) { console.log("在仓库中,CHANGE_COUNT执行,", state, payload); state.count += payload.num; },
vuex中提供
mapMutations
对象来辅助修改数据 , 由于是辅助函数修改,在事件中直接书写修改的函数名并传递参数
<template>
<div>
<p>{{ count }}p>
<p>{{ user }}p>
<button @click="CHANGE_COUNT(4)">修改countbutton>
<button @click="changeNum(5)">修改countbutton>
div>
template>
<script>
// 在单独构建的版本中辅助函数为 Vuex.mapState
import { mapState, mapMutations } from "vuex";
export default {
// 通过计算属性,返回数据
computed: {
...mapState(["count", "user"]),
},
methods: {
// 辅助函数,修改数据
...mapMutations(["CHANGE_COUNT"]),
// 辅助函数,采用对象方式修改
...mapMutations({
changeNum: "CHANGE_COUNT",
}),
},
};
script>
<style scoped>style>
作用: 相当于仓库中的计算属性, 对state进行二次处理,和state的作用几乎一样
// 导入vuex
import { createStore } from "vuex";
// 创建一个新的 store 实例
const store = createStore({
// 初始化state函数,其中保存的是所有组件共享的数据
state() {
return {
count: 100,
user: {
name: "张无忌",
age: 23,
sex: "男",
addr: "冰火岛",
},
vipArr: [
{ name: "张无忌", age: 23 },
{ name: "张三丰", age: 80 },
{ name: "金毛狮王", age: 60 },
{ name: "成昆", age: 60 },
],
};
},
// 仓库中的计算属性
getters: {
// 对vips数据进行处理,过滤器年龄超过60岁的人
vips(state) {
return state.vipArr.filter((v) => v.age >= 60);
},
},
// 提供修改state中数据的方法
mutations: {
// state,就是仓库对象
// payload修改的值
// 一般为了区分,会将这些方法名全部大写
CHANGE_COUNT(state, payload) {
console.log("在仓库中,CHANGE_COUNT执行,", state, payload);
state.count += payload;
},
CHANGE_USER(state, payload) {
state.user.age += payload.num;
},
},
});
// 导出
export default store;
getters 本身就是对数据进行二次处理的,没有辅助函数方式
<template>
<div>
<h3>花甲之年,华山之巅h3>
<p v-for="v in vips" :key="v.name">{{ v }}p>
div>
template>
<script>
import { mapGetters } from "vuex";
export default {
// 获取vuex数据,建议使用计算属性,更加便捷
computed: {
...mapGetters(["vips"]),
},
created() {
// 直接通过属性访问
console.log(this.$store.getters.vips);
},
};
script>
<style lang="less" scoped>style>
作用: 异步修改数据. mutations修改数据是同步的. 如果想要异步修改数据, 就放在actions中.
Action 类似于 mutation,不同在于:
// 导入vuex
import { createStore } from "vuex";
// 创建一个新的 store 实例
const store = createStore({
// 初始化state函数,其中保存的是所有组件共享的数据
state() {
return {
count: 100,
user: {
name: "张无忌",
age: 23,
sex: "男",
addr: "冰火岛",
},
vipArr: [
{ name: "张无忌", age: 23 },
{ name: "张三丰", age: 80 },
{ name: "金毛狮王", age: 60 },
{ name: "成昆", age: 60 },
],
// 空数组 商品数据
goods: [],
};
},
// 仓库中的计算属性
getters: {
// 对vips数据进行处理,过滤器年龄超过60岁的人
vips(state) {
return state.vipArr.filter((v) => v.age >= 60);
},
},
actions: {
// 异步修改数据
// context 相当于 this.$store
// payload 数据
ASYNC_SET_GOODS(context, payload) {
setTimeout(() => {
context.commit("SET_GOODS", payload);
}, 2000);
},
},
// 提供修改state中数据的方法
mutations: {
SET_GOODS(state, payload) {
// 同步修改数据
state.goods = payload;
},
// state,就是仓库对象
// payload修改的值
// 一般为了区分,会将这些方法名全部大写
CHANGE_COUNT(state, payload) {
console.log("在仓库中,CHANGE_COUNT执行,", state, payload);
state.count += payload;
},
CHANGE_USER(state, payload) {
state.user.age += payload.num;
},
},
});
// 导出
export default store;
其实不用actions,直接在mutations也可以完成异步修改数据的。只是actions是专门设计出来用于进行异步修改。
而在actions中最终还是需要使用mutations中的方法修改数据
触发修改数据与 mutations 修改基本一致,只需要将commit修改为dispatch即可
this.$store.dispatch ("函数名1", 数据);
this.$store.dispatch ({
type: '函数名1',
数据
});
import { mapActions } from 'vuex';
export default {
methods: {
...mapActions(["函数名"]) // 数组写法
...mapActions({ // 对象的写法
键名: "函数名"
})
}
}
作用: 拆分仓库, 把一个仓库拆分成几份
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}
const store = createStore({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
获取数据的时候,在正常操作的时候,加上模块名即可
this.$store.state.模块名.count
this.$store.commit("模块名/函数名", 数据)