為什么使用狀態(tài)管理
多個組件可能會依賴同一個狀態(tài)時,我們有必要抽取出組件內(nèi)的共同狀態(tài)集中統(tǒng)一管理,存放在一個全局單例中,這樣任何位置上的組件都可以訪問其中的狀態(tài)或觸發(fā)動作
簡單的store模式
通過自定義一個store模式實現(xiàn)全局的狀態(tài)管理,實例如下
有兩個組件a、b共享store和store2兩個狀態(tài),我們將其抽離在一個全局單例中,代碼如下:
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兩個值,在一個組件中對值進(jìn)行更新,在其他組件中對應(yīng)的值也會發(fā)生改變。
同時關(guān)閉a、b組件后,值依然保存,重新加載兩個組件,原來的狀態(tài)值存在。
重新刷新后,store和store2會變?yōu)槌跏贾?,如果需要做持久化,則需要再使用localstorage等進(jìn)行存儲。
服務(wù)器渲染(SSR)
這種簡單store模式下,可能會出現(xiàn)跨請求狀態(tài)污染。
在 SSR 環(huán)境下,應(yīng)用模塊通常只在服務(wù)器啟動時初始化一次。同一個應(yīng)用模塊會在多個服務(wù)器請求之間被復(fù)用,而我們的單例狀態(tài)對象也一樣。如果我們用單個用戶特定的數(shù)據(jù)對共享的單例狀態(tài)進(jìn)行修改,那么這個狀態(tài)可能會意外地泄露給另一個用戶的請求。我們把這種情況稱為跨請求狀態(tài)污染。
pinia
簡介
pinia在設(shè)計時考慮了ssr,參考這里(服務(wù)端渲染 (SSR) | Pinia (vuejs.org))
Vuex是一個專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式 + 庫。它采用集中式存儲管理應(yīng)用的所有組件的狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測的方式發(fā)生變化。
Pinia 最初正是為了探索 Vuex 的下一個版本而開發(fā)的,整合了vue核心團隊關(guān)于 Vuex 5 的許多想法。
相比于 Vuex,Pinia 提供了更簡潔直接的 API,并提供了組合式風(fēng)格的 API,最重要的是,在使用 TypeScript 時它提供了更完善的類型推導(dǎo)。
示例
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插件來實現(xiàn)數(shù)據(jù)的持久化,默認(rèn)進(jìn)行l(wèi)ocalstorage方式持久化存儲(pinia只是狀態(tài)管理庫,默認(rèn)是不會進(jìn)行數(shù)據(jù)持久化的)
當(dāng)然,有很多的存儲方法,比如vueuse的 useLocalStorage
方法,但是為什么需要用到pinia-plugin-persistedstate呢,官方文檔給出了理由:
pinia-plugin-persistedstate
旨在通過一致的 API 為每個人和每個項目中的 Pinia Store 提供持久化存儲。如果你希望保存一個完整的 Store,或者需要細(xì)?;渲?storage 和序列化的方式,該插件都為你提供了相應(yīng)的功能,并且可以在你想要持久化的 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實現(xiàn)插件全局注冊以及初始化和配置,即使得全局的pinia都使用了piniaPluginPersistedstate插件
3. 定義
在/stores/modules/user文件,可以直接通過defineStore方法進(jìn)行定義,狀態(tài)存在state中。
actions中是對state的一些操作和方法,其他組件通過這些方法操作state,保證狀態(tài)以一種可預(yù)測的方式發(fā)生變化。
getters中是獲取state的一些方法,在這些方法中可以對state做一些預(yù)處理和變化再傳遞給調(diào)用的組件
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內(nèi)容如下:
import { PersistedStateOptions } from "pinia-plugin-persistedstate";
/**
* @description pinia 持久化參數(shù)配置
* @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 中哪些數(shù)據(jù)需要被持久化。[]
表示不持久化任何狀態(tài),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來判斷當(dāng)前登錄狀態(tài)
storeToRefs
解構(gòu)賦值后的變量會喪失響應(yīng)性,使用storeToRefs方法可以使變量重新獲得響應(yīng)性。
注意這里的變量應(yīng)該指的是基本數(shù)據(jù)類型,如果是對象,則會保留響應(yīng)性。
例如:定義如下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>
結(jié)果如圖,此時token和userInfo都是具有響應(yīng)性的,使用changeToken函數(shù)后store中的token和解構(gòu)的token都會發(fā)生變化
但是如果將storeToRefs去掉文章來源:http://www.zghlxwxcb.cn/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("失去響應(yīng)的", token);
console.log("userStore.token", userStore.token);
};
</script>
結(jié)果如圖,token將喪失響應(yīng)性
此時調(diào)用changeToken函數(shù),只有userStore.token會變化,解構(gòu)的token不會變化
但是對于userInfo來說,無論是修改store中的name——userStore.setName(‘456’)",還是直接修改解構(gòu)的userInfo——userInfo.name = ‘zcc’,上述代碼中的三項不同方式獲取的name都會同時發(fā)生改變。文章來源地址http://www.zghlxwxcb.cn/news/detail-637176.html
到了這里,關(guān)于vue 全局狀態(tài)管理(簡單的store模式、使用Pinia)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!