Vuex 状态管理
概述
Vuex 是一个专门为 Vue 开发的状态管理器。Vuex 采用集中式存储管理应用的多个组件之间的共享状态数据,并以相应的规则保证状态以一种可预测的方式发生变化。
通过使用 Vuex,可以轻松地管理 Vue 应用程序的状态,并确保所有组件都可以方便地访问和修改该状态。
安装框架
npm install vuex@next --save
核心概念
Vuex 包含7个核心概念:
- store:管理对象,仅有一个。
- state:存储状态数据。
- getters:基于state的计算属性。
- mutations:用于修改状态的方法,只能进行同步操作。
- actions:可以同步或异步间接更新状态的方法。
- modules:将store拆分为模块,每个模块都有自己的state、getters、mutations和actions。
- plugins:配置插件。
用法
基本用法
配置 store,在 src/store 目录中新建 index.js 文件:
import {createStore} from 'vuex';
const store = createStore({
// 状态
state: {
count: 0
},
// 计算属性
getters: {
addOrEven(state) {
return state.count % 2 === 1 ? "奇数" : "偶数";
}
},
// 更新状态方法
mutations: {
increase(state, num) {
state.count += num;
},
decrease(state, num) {
state.count -= num;
}
},
// 异步或同步更新状态方法
actions: {
increaseAsync({commit}, num) {
setTimeout(() => {
commit('increase', num);
}, 1000);
},
decreaseAsync(context, num) {
setTimeout(() => {
context.commit('decrease', num);
}, 1000);
},
},
});
export default store;
配置 main.js 文件:
import {createApp} from "vue";
import "./style.css";
import App from "./App.vue";
import store from "@/store/index.js";
const app = createApp(App);
app.use(store);
app.mount("#app");
创建2个组件:
CountShow.vue:
<script setup>
import {useStore} from "vuex";
const store = useStore();
console.log("store:", store);
</script>
<template>
<div>
<h2>CountShow</h2>
<p>count: {{ store.state.count }}</p>
<p>奇数或偶数:{{ store.getters.addOrEven }}</p>
</div>
</template>
<style scoped>
div {
margin: 10px;
border: 1px solid blue;
}
</style>
CountUpdate.vue:
<script setup>
import {useStore} from "vuex";
const store = useStore();
const increase = () => {
store.commit("increase", 1);
};
const decrease = () => {
store.commit("decrease", 1);
};
const increaseAsync = () => {
store.dispatch("increaseAsync", 2);
};
const decreaseAsync = () => {
store.dispatch("decreaseAsync", 2);
};
</script>
<template>
<div>
<h2>CountUpdate</h2>
<button @click="increase">count++</button>
<button @click="decrease">count--</button>
<br>
<button @click="increaseAsync">count+2(异步执行)</button>
<button @click="decreaseAsync">count-2(异步执行)</button>
</div>
</template>
<style scoped>
div {
margin: 10px;
border: 1px solid red;
}
button {
margin: 5px;
}
</style>
使用这2个组件:
<script setup>
import CountShow from "@/components/CountShow.vue";
import CountUpdate from "@/components/CountUpdate.vue";
</script>
<template>
<CountShow/>
<CountUpdate/>
</template>
多模块开发
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块。
定义子模块,在 store 目录下新建 user/index.js 文件:
const user = {
state: {
name: "小明",
age: 18
},
getters: {
ageOddOrEven(state) {
return state.age % 2 === 1 ? "奇数" : "偶数";
}
},
mutations: {
increaseAge(state, num) {
state.age += num;
},
changeName(state, name) {
state.name += name;
}
},
actions: {
increaseAgeAsync(context, num) {
setTimeout(() => {
context.commit('increaseAge', num);
}, 1000);
},
changeNameAsync({commit}, name) {
setTimeout(() => {
commit('changeName', name);
}, 1000);
}
}
};
export default user;
在 store 中配置多模块:
import {createStore} from 'vuex';
import user from "@/store/user/index.js";
const store = createStore({
modules: {
user
}
});
export default store;
使用:
<script setup>
import store from "./store/index.js";
const changeAge = () => {
store.commit("increaseAge", 1);
};
const changeName = () => {
store.commit("changeName", "a");
};
const changeAgeAsync = () => {
store.dispatch("increaseAgeAsync", 2);
};
const changeNameAsync = () => {
store.dispatch("changeNameAsync", "b");
};
</script>
<template>
<p>name: {{ store.state.user.name }}</p>
<p>age: {{ store.state.user.age }}</p>
<p>age是奇数还是偶数: {{ store.getters.ageOddOrEven }}</p>
<button @click="changeAge">修改age</button>
<button @click="changeName">修改name</button>
<button @click="changeAgeAsync">修改age(异步执行)</button>
<button @click="changeNameAsync">修改name(异步执行)</button>
</template>
命名空间
在多个Vuex模块中有相同名称的mutation方法或action方法,那么一旦在组件中调用store对象的commit方法或dispatch方法,所有模块中匹配对应名称的mutation方法和action方法就都会执行
Vuex 支持命名空间,方便指定使用某个模块。
定义子模块,在 store 目录下新建 student/index.js 文件:
const student = {
namespaced: true, // 开启命名空间
state: {
age: 18
},
getters: {
oddOrEven(state) {
return state.age % 2 === 1 ? "奇数" : "偶数";
}
},
mutations: {
increaseAge(state, num) {
state.age += num;
}
},
actions: {
increaseAgeAsync(context, num) {
setTimeout(() => {
context.commit('increaseAge', num);
}, 1000);
}
}
};
export default student;
配置多模块:
import {createStore} from 'vuex';
const store = createStore({
modules: {
student
}
});
export default store;
使用:
<script setup>
import store from "./store/index.js";
const changeStuAge = () => {
store.commit("student/increaseAge", 1);
};
const changeStuAgeAsync = () => {
store.dispatch("student/increaseAgeAsync", 2);
};
</script>
<template>
<p>student age:{{ store.state.student.age }}</p>
<p>student name:{{ store.getters["student/oddOrEven"] }}</p>
<button @click="changeStuAge">修改age</button>
<button @click="changeStuAgeAsync">修改age(异步执行)</button>
</template>
持久化处理
可以借助第三方插件 vuex-persistedstate 实现持久化,存储在 LocalStorage 中。
下载工具包:
npm install vuex-persistestate
在 store 中配置持久化插件:
import {createStore} from 'vuex';
import user from "@/store/user/index.js";
import student from "@/store/student/index.js";
import createPersistedState from "vuex-persistedstate";
const store = createStore({
modules: {
user,
student,
},
// 配置插件
plugins: [
// createPersistedState() // 持久化缓存所有状态
createPersistedState({
paths:["student"], // 只缓存student模块
})
]
});
export default store;