介绍
在 Vue 3 中,状态管理是构建大型应用不可或缺的一部分。Pinia 是一个专为 Vue 3 设计的状态管理库,它提供了一种简洁、优雅的方式来管理组件间共享的状态。本文将介绍 Pinia 的基本概念、安装配置方法以及如何在 Vue 应用中使用 Pinia 进行状态管理,并且探讨如何集成持久化插件来确保应用状态的持久性。
什么是 Pinia?
Pinia 是一个基于 Vue 3 的状态管理库,它提供了一种响应式、强类型的方式来管理应用的状态。与 Vuex 不同,Pinia 鼓励使用独立的 store 实例来管理状态,每个 store 实例都可以包含自己的状态、mutations、actions 等。
对比 Vuex
Pinia 起源于一次探索 Vuex 下一个迭代的实验,因此结合了 Vuex 5 核心团队讨论中的许多想法。最后,我们意识到 Pinia 已经实现了我们在 Vuex 5 中想要的大部分功能,所以决定将其作为新的推荐方案来代替 Vuex。
与 Vuex 相比,Pinia 不仅提供了一个更简单的 API,也提供了符合组合式 API 风格的 API,最重要的是,搭配 TypeScript 一起使用时有非常可靠的类型推断支持。
安装 Pinia
要开始使用 Pinia,首先确保你的项目已经安装了 Vue 3。
yarn add pinia
# 或者使用 npm
npm install pinia
安装 Pinia 持久化插件
pinia-plugin-persistedstate插件兼容 pinia^2.0.0,在使用之前请确保你已经安装 Pinia
yarn add pinia-plugin-persistedstate
# 或者使用 npm
npm install pinia-plugin-persistedstate
- 与
vuex-persistedstate相似的 API - 所有 Store 均可单独配置
- 自定义 storage 和数据序列化
- 恢复持久化数据前后的 hook
- 每个 Store 具有丰富的配置
- 兼容 Vue 2 和 3
- 无任何外部依赖
配置 Pinia
// stores/index.ts
import { createPinia } from "pinia";
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
// piniaPersist(持久化)
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
export default pinia;
// main.ts
import { createApp } from "vue";
import App from "./App.vue";
import pinia from "@/stores/index";
const app = createApp(App);
app.use(pinia).mount("#app");
创建 Store
在 Pinia 中,每个 store 都被视为一个独立的实例。我们可以通过 defineStore 函数来创建一个 store。
官方提供两种方法创建来Store,一种是选项式API、一种是函数式API。
Option StoreSetup Store
Option Store
与 Vue 的选项式 API 类似,我们也可以传入一个带有 state、actions 与 getters 属性的 Option 对象
import { defineStore } from "pinia";
// types
interface GlobalStoreType {
token: string;
userInfo: any;
}
// 你可以任意命名 `defineStore()` 的返回值,但最好使用 store 的名字,同时以 `use` 开头且以 `Store` 结尾。
// defineStore 调用后返回一个函数,调用该函数获得 Store 实体
export const useGlobalStore = defineStore({
// id: 必须的,在所有 Store 中唯一
id: "globalState",
// state: 返回对象的函数
state: (): GlobalStoreType => ({
// token
token: "",
// userInfo
userInfo: {},
}),
getters: {
getToken(): string {
return this.token;
},
getUserInfo(): any {
return this.userInfo;
}
},
actions: {
// setToken
setToken(token: string) {
this.token = token;
},
// setUserInfo
setUserInfo(userInfo: any) {
this.userInfo = userInfo;
}
},
persist: true,// 开启持久化
});
你可以认为 state 是 store 的数据 (data),getters 是 store 的计算属性 (computed),而 actions 则是方法 (methods)。
为方便上手使用,Option Store 应尽可能直观简单。
Setup Store
与 Vue 组合式 API 的 setup 函数 相似,我们可以传入一个函数,该函数定义了一些响应式属性和方法,并且返回一个带有我们想暴露出去的属性和方法的对象。
import { defineStore } from "pinia";
import { ref, computed } from "vue";
// 你可以任意命名 `defineStore()` 的返回值,但最好使用 store 的名字,同时以 `use` 开头且以 `Store` 结尾。
// defineStore 调用后返回一个函数,调用该函数获得 Store 实体
export const useGlobalStore = defineStore(
// id: 必须的,在所有 Store 中唯一
"globalState",
() => {
const token = ref("");
const userInfo = ref({});
const getToken = computed(() => token.value);
const getUserInfo = computed(() => userInfo.value);
const setToken = (val: string) => {
token.value = val;
};
const setUserInfo = (val: any) => {
userInfo.value = val;
};
return {
token,
userInfo,
getToken,
getUserInfo,
setToken,
setUserInfo
};
},
{
// 持久化
persist: true
}
);
在 Setup Store 中:
ref()就是state属性computed()就是gettersfunction()就是actions
注意,要让 pinia 正确识别 state,你必须在 setup store 中返回 state 的所有属性。
使用 Store
虽然我们前面定义了一个 store,但在我们使用 <script setup> 调用 useStore()(或者使用 setup() 函数,像所有的组件那样) 之前,store 实例是不会被创建的:
<script setup>
import { useGlobalStore } from '@/stores/user'
// 可以在组件中的任意位置访问 `store` 变量 ✨
const globalStore = useGlobalStore()
</script>
你可以定义任意多的 store,但为了让使用 pinia 的益处最大化 (比如允许构建工具自动进行代码分割以及 TypeScript 推断),你应该在不同的文件中去定义 store。
从 Store 解构
<script setup>
import { useGlobalStore } from '@/stores/user'
const globalStore = useGlobalStore()
// ❌ 这将不起作用,因为它破坏了响应性
const { token, userInfo } = globalStore
token // 将始终是 ""
userInfo // 将始终是 {}
// ✅ 这样写是响应式的
// 💡 当然你也可以直接使用 `store.token`
const tokenValue = computed(() => store.token)
</script>
为了从 store 中提取属性时保持其响应性,你需要使用 storeToRefs()。它将为每一个响应式属性创建引用。当你只使用 store 的状态而不调用任何 action 时,它会非常有用。请注意,你可以直接从 store 中解构 action,因为它们也被绑定到 store 上:
<script setup>
import { storeToRefs } from 'pinia'
import { useGlobalStore } from '@/stores/user'
const globalStore = useGlobalStore()
// `token` 和 `userInfo` 是响应式的 ref
// 同时通过插件添加的属性也会被提取为 ref
// 并且会跳过所有的 action 或非响应式 (不是 ref 或 reactive) 的属性
const { token, userInfo } = storeToRefs(globalStore)
// 作为 action 的 setToken 可以直接解构
const { setToken } = globalStore
</script>
Pinia 持久化配置
该插件的默认配置如下:
- 使用
localStorage进行存储 store.$id作为 storage 默认的 key- 使用
JSON.stringify/JSON.parse进行序列化/反序列化 - 整个 state 默认将被持久化
如果你不想使用默认的配置,那么你可以将一个对象传递给 Store 的 persist 属性来配置持久化。
import { defineStore } from 'pinia'
export const useStore = defineStore('main', {
state: () => ({
someState: '你好 pinia',
}),
persist: {
// 在这里进行自定义配置
},
})
总结
本文介绍了如何使用 Pinia 管理 Vue 应用状态,并通过创建 store 和使用 store 在组件中共享状态。此外,我们还探讨了如何集成 Pinia 的持久化插件来确保应用状态的持久性,使用户在刷新页面后不会丢失状态。
Pinia 提供了一种简单、直观的方式来管理 Vue 应用的状态,它的强类型支持和独立的 store 实例使得状态管理变得更加灵活和可维护。希望本文能够帮助你更好地理解和使用 Pinia。