为什么使用状态管理
多个组件可能会依赖同一个状态时,我们有必要抽取出组件内的共同状态集中统一管理,存放在一个全局单例中,这样任何位置上的组件都可以访问其中的状态或触发动作
简单的store模式
通过自定义一个store模式实现全局的状态管理,实例如下
有两个组件a、b共享store和store2两个状态,我们将其抽离在一个全局单例中,代码如下:
import { reactive } from "vue";
export const store = reactive({
count: 0
});
export const store2 = {
count: 0
};
a组件中:
<script setup>
import { store, store2 } from "./store.js";
console.log(store, store2);
</script>
<template>
<div @click="store.count++">From A: {{ store.count }}</div>
</template>
b组件中:
<script setup>
import { store, store2 } from "./store.js";
console.log(store, store2);
</script>
<template>
<div @click="store2.count++">From B: {{ store.count }}</div>
</template>
这样,a、b组件共享了store和store2两个值,在一个组件中对值进行更新,在其他组件中对应的值也会发生改变。
同时关闭a、b组件后,值依然保存,重新加载两个组件,原来的状态值存在。
重新刷新后,store和store2会变为初始值,如果需要做持久化,则需要再使用localstorage等进行存储。
服务器渲染(SSR)
这种简单store模式下,可能会出现跨请求状态污染。
在 SSR 环境下,应用模块通常只在服务器启动时初始化一次。同一个应用模块会在多个服务器请求之间被复用,而我们的单例状态对象也一样。如果我们用单个用户特定的数据对共享的单例状态进行修改,那么这个状态可能会意外地泄露给另一个用户的请求。我们把这种情况称为跨请求状态污染。
pinia
简介
pinia在设计时考虑了ssr,参考这里(服务端渲染 (SSR) | Pinia (vuejs.org))
Vuex是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
Pinia 最初正是为了探索 Vuex 的下一个版本而开发的,整合了vue核心团队关于 Vuex 5 的许多想法。
相比于 Vuex,Pinia 提供了更简洁直接的 API,并提供了组合式风格的 API,最重要的是,在使用 TypeScript 时它提供了更完善的类型推导。
示例
1. 定义一个index.ts文件
import { createPinia } from "pinia";
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
// pinia persist
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
export default pinia;
直接通过引入pinia-plugin-persistedstate插件来实现数据的持久化,默认进行localstorage方式持久化存储(pinia只是状态管理库,默认是不会进行数据持久化的)
当然,有很多的存储方法,比如vueuse的 useLocalStorage
方法,但是为什么需要用到pinia-plugin-persistedstate呢,官方文档给出了理由:
pinia-plugin-persistedstate
旨在通过一致的 API 为每个人和每个项目中的 Pinia Store 提供持久化存储。如果你希望保存一个完整的 Store,或者需要细粒化配置 storage 和序列化的方式,该插件都为你提供了相应的功能,并且可以在你想要持久化的 Store 上使用相同的配置。
2. 在main.ts中引入
import pinia from "@/stores";
import { createApp } from "vue";
import App from "./App.vue";
const app = createApp(App);
app.use(pinia).mount("#app");
通过app.use实现插件全局注册以及初始化和配置,即使得全局的pinia都使用了piniaPluginPersistedstate插件
3. 定义
在/stores/modules/user文件,可以直接通过defineStore方法进行定义,状态存在state中。
actions中是对state的一些操作和方法,其他组件通过这些方法操作state,保证状态以一种可预测的方式发生变化。
getters中是获取state的一些方法,在这些方法中可以对state做一些预处理和变化再传递给调用的组件
import { defineStore } from "pinia";
import { UserState } from "@/stores/interface";
import piniaPersistConfig from "@/config/piniaPersist";
export const useUserStore = defineStore({
id: "user",
state: (): UserState => ({
token: "",
userInfo: { name: "cc" }
}),
getters: {},
actions: {
// Set Token
setToken(token: string) {
this.token = token;
},
// Set setUserInfo
setUserInfo(userInfo: UserState["userInfo"]) {
this.userInfo = userInfo;
}
},
persist: piniaPersistConfig("user")
});
persist则是引入了piniaPluginPersistedstate持久化后的一些可配置信息
/config/piniaPersist内容如下:
import { PersistedStateOptions } from "pinia-plugin-persistedstate";
/**
* @description pinia 持久化参数配置
* @param {String} key 存储到持久化的 name
* @param {Array} paths 需要持久化的 state name
* @return persist
* */
const piniaPersistConfig = (key: string, paths?: string[]) => {
const persist: PersistedStateOptions = {
key,
storage: localStorage,
// storage: sessionStorage,
paths
};
return persist;
};
export default piniaPersistConfig;
paths用于指定 state 中哪些数据需要被持久化。[]
表示不持久化任何状态,undefined
或 null
表示持久化整个 state。
更多配置信息可以参考官方文档prazdevs.github.io/pinia-plugin-persistedstate/zh/guide/config.html
4. 使用
import { useUserStore } from "@/stores/modules/user";
const userStore = useUserStore();
router.beforeEach( (to, from, next) => {
if (userStore.token) return next(from.fullPath);
}
通过const userStore = useUserStore();
引入userStore,全局token来判断当前登录状态
storeToRefs
解构赋值后的变量会丧失响应性,使用storeToRefs方法可以使变量重新获得响应性。
注意这里的变量应该指的是基本数据类型,如果是对象,则会保留响应性。
例如:定义如下store
export const useUserStore = defineStore({
id: "user",
state: (): UserState => ({
token: "",
userInfo: { name: "Geeker" }
}),
getters: {
getUserInfo(state) {
return state.userInfo;
}
},
actions: {
// Set Token
setToken(token: string) {
this.token = token;
},
// Set setUserInfo
setUserInfo(userInfo: UserState["userInfo"]) {
this.userInfo = userInfo;
},
setName(name: string) {
this.userInfo.name = name;
}
},
persist: piniaPersistConfig("user")
});
在其他组件中引入
<script setup lang="ts">
import { computed } from "vue";
import { storeToRefs } from "pinia";
import { useUserStore } from "@/stores/modules/user";
const userStore = useUserStore();
const username = computed(() => userStore.userInfo.name);
const { userInfo } = userStore;
console.log(userInfo);
const { token } = storeToRefs(userStore);
console.log(token);
const changeToken = () => {
userStore.setToken("123" + Math.random());
console.log(token);
};
</script>
<template>
<span class="username">{{ username }}</span>
<span @click="userStore.setName('456')">{{ userStore.getUserInfo.name }}</span>
<span @click="userInfo.name = 'zcc'" class="username">{{ userInfo.name }}</span>
<span @click="changeToken">{{ token }}</span>
</template>
结果如图,此时token和userInfo都是具有响应性的,使用changeToken函数后store中的token和解构的token都会发生变化
但是如果将storeToRefs去掉文章来源:https://www.toymoban.com/news/detail-637176.html
<script setup lang="ts">
import { computed } from "vue";
import { useUserStore } from "@/stores/modules/user";
const userStore = useUserStore();
const username = computed(() => userStore.userInfo.name);
const { userInfo } = userStore;
console.log(userInfo);
const { token } = userStore;
console.log(token);
const changeToken = () => {
userStore.setToken("123" + Math.random());
console.log("失去响应的", token);
console.log("userStore.token", userStore.token);
};
</script>
结果如图,token将丧失响应性
此时调用changeToken函数,只有userStore.token会变化,解构的token不会变化
但是对于userInfo来说,无论是修改store中的name——userStore.setName(‘456’)",还是直接修改解构的userInfo——userInfo.name = ‘zcc’,上述代码中的三项不同方式获取的name都会同时发生改变。文章来源地址https://www.toymoban.com/news/detail-637176.html
到了这里,关于vue 全局状态管理(简单的store模式、使用Pinia)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!