前言
本文适合有前端基础并有一定使用库经验的 jym 快速查阅,并不适合纯小白观看阅读(因为小白体验差)!!!
什么是状态管理?
状态管理指的是在多个组件有效的管理和维护数据的状态,也就是说在需要数据在多个组件之间共享和传递的时候,我们就是可以使用状态管工具来保证数据的一致性、可追踪性和可预测性。
1. Vuex
1.1 核心概念
1.1.1 State(状态)
作用:保存需要共享的数据,类似组件中的data
在 Vue2 中
方法一:在需要的组件中使用 this.$store.state 获取状态对象下的数据
方法二:使用辅助函数 mapState
import { mapState } from 'vuex';
computed: {
// 使用 mapState 将状态映射到组件的计算属性中
// 接收的是一个数组,要传入 state 中多个状态值的话要以逗号分隔
...mapState(['hobby'])
//另一种写法
...mapState({
'hobby': state => state.hobby
})
},
// 最后可以直接在模板中使用
<template>
<div>
<p>我的爱好是: {{ hobby }}</p>
</div>
</template>
在 Vue3 中
方法一:在需要的组件中使用 store.state 获取状态对象下的数据
import { useStore } from 'vuex';
const store = useStore();
const hobby = store.state.hobby;
})
同理:
<script setup>
import { useStore } from 'vuex';
import { computed } from 'vue';
const store = useStore();
const hobby = computed(() => {
return store.state.hobby;
})
方法二:使用辅助函数 mapState
<script setup>
import { useStore } from 'vuex';
import { computed } from 'vue';
const store = useStore();
// 使用 mapState 将状态映射到组件的计算属性中
const { hobby } = mapState(['hobby']);
</script>
// 最后可以直接在模板中使用
<template>
<div>
<p>我的爱好是: {{ hobby }}</p>
</div>
</template>
1.1.2 Getters(获取器)
作用:相当于计算属性,可以对state 进行计算、过滤、转换,并保持响应式
在 Vue2 中
方法一:在需要的组件中使用 this.$store.getters 获取
方法二:使用辅助函数 mapGetters
import { mapGetters } from 'vuex';
computed: {
...mapGetters(['getHobby'])
}
// 最后可以直接在模板中使用
<template>
<div>
<p>我的爱好是: {{ getHobby }}</p>
</div>
</template>
在 Vue3 中
方法一:在需要的组件中使用 store.getters 获取
import { useStore } from 'vuex';
const store = useStore();
const hobby = store.getters.getHobby
})
同理:
import { useStore } from 'vuex';
import { computed } from 'vue';
const store = useStore();
const hobby = computed(() => {
return store.getters.getHobby;
})
方法二:使用辅助函数 mapGetters
<script setup>
import { useStore, mapGetters } from 'vuex';
const store = useStore();
// 不改变 getter 属性名的方式
const { getHobby } = mapGetters(['getHobby']);
// 将 getter 属性取另外一个名字的方式
const { getHobby: getHobbyVal } = mapGetters({ getHobby:'getHobbyVal'});
</script>
// 最后可以直接在模板中使用(使用另取名字的方式)
<template>
<div>
<p>我的爱好是: {{ getHobbyVal }}</p>
</div>
</template>
1.1.3 Mutations(突变)
作用:同步修改 state 的值(使用 commit 调用)
在 Vue2 中
方法一:直接使用 $store.commit 传入额外的参数
this.$store.commit('updateHobby','小军')
方法二:使用 mapMutations 辅助函数
import { mapMutations } from 'vuex'
methods: {
// 映射到组件的方法中,接受的参数是一个数组,多个方法以逗号分隔
...mapMutations(['updateHobby']),
// 将 updateHobby 映射另外一个名字的方式
...mapMutations({
setHobby: 'updateHobby'
}),
// 在其他要使用的方法中
otherMethod(){
this.updateHobby('小军')
}
}
在 Vue3 中
方法一:直接使用 store.commit 传入额外的参数
import { useStore } from 'vuex';
const store = useStore();
store.commit('updateHobby','小军')
方法二:使用 mapMutations 辅助函数
<script setup>
import { mapMutations } from 'vuex';
// 使用 mapMutations 接受的参数是一个数组,多个方法以逗号分隔
const updateHobby = mapMutations(['updateHobby']);
// 将 updateHobby 映射另外一个名字的方式
const updateHobby = mapMutations({ setHobby:'updateHobby'});
// 在其他方法中直接调用
function otherMethod() {
setHobby('小军');
}
</script>
1.1.4 Actions(动作)
作用:异步修改 state 的值(使用 dispatch 调用)
在 Vue2 中
方法一:直接使用 $store.dispatch 触发
this.$store.dispatch('asyncUpdateHobby','小军')
方法二:使用 mapActions 辅助函数
import { mapActions } from 'vuex'
methods: {
// 使用 mapActions 接受的参数是一个数组,多个方法以逗号分隔
...mapActions(['asyncUpdateHobby']),
// 将 updateHobby 映射另外一个名字的方式
...mapActions({
asyncSetHobby: 'asyncUpdateHobby'
}),
// 在其他方法中直接调用
otherMethod(){
asyncUpdateHobby('小军')
}
}
在 Vue3 中
方法一:直接使用 store.dispatch 触发
import { useStore } from 'vuex';
const store = useStore();
store.dispatch('asyncUpdateHobby','小军')
方法二:使用 mapActions 辅助函数
<script setup>
import { mapActions } from 'vuex';
// 使用 mapActions 接受的参数是一个数组,多个方法以逗号分隔
const asyncUpdateHobby = mapActions(['asyncUpdateHobby']);
// 将 asyncUpdateHobby 映射另外一个名字的方式
const asyncUpdateHobby = mapActions({ asyncSetHobby:'asyncUpdateHobby'});
// 在其他方法中直接调用
function otherMethod() {
asyncSetHobby('小军');
}
</script>
1.1.5 Modules(模块)
作用:将应用的状态分成不同模块,使得所有模块状态独立,避免 store 对象臃肿
在 Vue2 中
使用 modulesA 下的 getters 的 xxxx
// a 是 store 下更改的 modulesA 对应的名称
this.$store.getters['a/xxxx']
使用 modulesB 下的 actions 的 xxxx
this.$store.dispatch('b/xxxx', 123);
在 Vue3 中
import { useStore } from 'vuex';
import { computed } from 'vue';
const store = useStore();
// 使用模块A下的getters
const moduleAGetter = computed(() => store.getters['a/xxxx']);
// 使用模块B下的actions
const moduleBAction = () =>{
store.dispatch('b/xxxx', 123);
}
1.2 Vue2写法(配置 Vuex)
1.2.1 自定义的模块(不使用时 modules 可忽略)
在`src/store/modules/moduleA.js`中
const state = { ...... };
const getters = { ...... };
const mutations = { ...... };
const actions = { ...... };
export default {
namespaced: true,//开启命名空间
state,
mutations,
actions,
getters,
};
在 `src/store/modules/moduleB.js`中
与 moduleA 同上省略...
1.2.2 在 store 文件夹下的 index.js
import Vue from 'vue';
import Vuex from 'vuex';
//需要使用 modules 的时候
import moduleA from './modules/moduleA';
import moduleB from './modules/moduleB';
// 注册 Vuex
Vue.use(Vuex);
const state = {
hobby:'敲代码'
};
const getters = {
getHobby(state) {
return state.hobby;
},
};
const mutations = {
updateHobby(state,payload) {
// payload指的是传递的数值
// 大多时候应该为一个对象,因为可以包含多个字段
state.hobby = payload;
},
};
const actions = {
asyncUpdateHobby(ctx, payload) {
// ctx指的是上下文对象context
ctx.commit("updateHobby", payload);
},
// 第二种方式(参数解构)
asyncUpdateHobby({ commit, state, dispatch}, payload){
commit('updateHobby',payload);
dispatch('otherAsyncFn',payload);
}
};
export default new Vuex.Store({
state,
getters,
mutations,
actions,
// 需要使用 modules 的时候
modules:{
a:moduleA,// 更改模块名
b:moduleB // 更改模块名
}
});
1.2.3 在 main.js 中
import Vue from 'vue'
import store from 'store/index.js'
new Vue({
el:'#app',
router,
store,
render:h=>h(App)
})
1.3 Vue3写法(配置 Vuex)
1.3.1 自定义的模块(不使用时 modules 可忽略)
子模块的配置相同,具体内容如上(1.2.1)
1.3.2 在 store 文件夹下的 index.js
import { createStore } from "vuex";
//需要使用 modules 的时候
import moduleA from './modules/moduleA';
import moduleB from './modules/moduleB';
// 创造store实例
const store = createStore({
state() {
return {
hobby:"敲代码",
};
},
getters: {
getHobby(state) {
return state.hobby;
},
},
mutations: {
updateHobby(state, payload) {
// payload指的是传递的数值
// 大多时候应该为一个对象,因为可以包含多个字段
state.hobby = payload;
},
},
actions: {
asyncUpdateHobby(ctx, payload) {
// ctx指的是上下文对象context
ctx.commit("updateHobby", payload);
},
},
// 需要使用 modules 的时候
modules:{
a:moduleA,// 更改模块名
b:moduleB // 更改模块名
}
});
export default store
1.3.3 在 main.js 中
import App from "./App";
import router from "./router";
import { createApp } from "vue";
//引入vuex
import store from 'store/index.js'
const app = createApp(App);
app.use(router).use(store).mount("#app");
2. Pinia
注:该部分仅演示 Vue3 下的代码
2.1 配置 store
/** src/store/index.ts **/
import { createPinia } from 'pinia';
// 要实现 存储状态并持久化 可安装并导入插件
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
const store = createPinia();
store.use(piniaPluginPersistedstate); //不用插件可忽略
export default store
/** src/main.ts **/
import { createApp } from 'vue';
import App from './App.vue';
import store from './store';
const app = createApp(App);
app.use(store);
/** src/store/info.js **/
import { defineStore } from 'pinia';
// defineStore 第一个参数:唯一id 第二个参数:配置对象或 setup 函数
export const xxxStore = defineStore('useInfoStore',{
state: () => {
return {
hobby: '敲代码',
age: 23
}
},
getters:{
// 也可以直接使用 this 访问其他的 getter 属性
double:(state) => state.age * 2,
},
// pinia 中已经取消了 mutations, 统一使用 actions
// 在 actions 中 同步异步都可以,也可以在这里调用 API 和其他 action 方法
actions: {
setHobby(val) {
// 通过this访问整个 store 实例
this.hobby = val;
},
},
// 持久化配置 (直接设置为 true 表示所有状态数据持久化)
persist: {
// 修改当前 Store 的 id
key: 'storekey',
// 修改为 sessionStorage,默认为 localStorage
storage: window.sessionStorage,
// 持久化状态路径
// 通过点符号路径,指定缓存的字段,默认是所有状态数据
paths: ['hobby'],
},
})
// 注:若传递给 defineStore 的是 setup 函数
// 那么 ref()就是 state 属性
// computed()就是 getters
// function()就是 actions
2.2 核心概念
2.2.1 State(状态)
作用:存储仓库中的数据,当状态发生改变的时候,相关联的组件自动更新
<script setup>
// 引入自定义的 store
import { useInfoStore } from "@/store/info";
// 需要解构时
import { storeToRefs } from 'pinia';
// 实例化
const info = useInfoStore();
/** 修改 **/
方法一:直接修改
info.hobby = '唱歌';
方法二:调用 $patch(可同时修改多个值)
info.$patch({
hobby:'唱歌'
})
// 在需要进行额外操作来实现修改的情况下,推荐传递一个函数
info.$patch(state =>{
// ......
})
/** 重置(恢复为初始值) **/
info.$reset();
/** 解构(直接解构会失去响应式) **/
const { hobby } = storeToRefs(info);
/** 替换(覆盖原有状态) **/
info.$state = {
hobby:'唱歌'
}
</script>
/** 访问(模板上) 无需辅助函数**/
<template>
<div>
<p>我的爱好还是{{info.hobby}}哦</p>
</div>
</template>
2.2.2 Getter(获取器)
作用:从状态派生计算属性的方法
<script setup>
// 引入自定义的 store
import { useInfoStore } from "@/store/info";
// 实例化
const info = useInfoStore();
/** 访问--- 跟state属性完全一样 **/
/** 使用其他 Store 的 Getter **/
// 引入其他的 store
import { useOtherStore } from "@/store/other";
// 实例化
const other = useOtherStore();
</script>
2.2.3 Action(操作)
作用:修改状态的方法,可以执行异步操作,并且可以做直接使用 this 来访问仓库中的状态和其他操作
<script setup>
// 引入自定义的 store
import { useInfoStore } from "@/store/info";
const store = useInfoStore();
// 直接调用
store.setHobby('唱歌');
</script>
/** 模板上使用 **/
<template>
<button @click="store.setHobby('唱歌')">修改爱好</button>
</template>
2.2.4 Plugin(插件)
作用:包装 or 添加 新的属性方法
/** 添加 **/
import { createPinia } from 'pinia';
const store = createPinia();
//通过 use() 添加到实例
store.use(xxx);
/** 扩展 **/
// 给每个 store 都添加上特定属性
store.use(({store})=>{
store.job = '程序员'
});