vue3中使用Pinia
Pinia 是一个用于 Vue 的状态管理库,类似 Vuex, 是 Vue 的另一种状态管理方案
三大核心:state(存储的值),getters(计算属性),actions也可支持同步(改变值的方法,支持同步和异步)
npm install pinia@next
or
yarn add pinia@next
模块化封装
创建实例
新建 store/index.ts(src目录下新建store文件夹,并创建index.ts文件)
创建 pinia 实例并导出所有模块
import { createPinia } from 'pinia'
const store = createPinia()
export default store
//main.ts
// 引入 pinia
import store from './stores'
const app = createApp(App)
app.use(store)
module
新建store/modules/urer.ts
modules 按模块定义对应的 store
, user.ts
表示用户相关数据存储的仓库
// user.ts
import { defineStore } from 'pinia'
// 你可以对 `defineStore()` 的返回值进行任意命名,但最好使用 store 的名字,同时以 `use` 开头且以 `Store` 结尾。(比如 `useUserStore`,`useCartStore`,`useProductStore`)
// 第一个参数是你的应用中 Store 的唯一 ID。
export const useUserInfoStore = defineStore('useUserInfoStore', {
// 其他配置...
// state: 返回对象的函数
state: () => {
return {
login: false
}
},
getters: {
newName: (state) => state.userInfo.name + 'vip',
},
actions: {
//更新整个对象
updateUserInfo(userInfo: { name: string; age: number }) {
this.userInfo = userInfo;
},
//更新对象中某个属性
updateAge(age: number) {
this.userInfo.age = age;
},
//更新基础数据类型
updateToken(token: string) {
this.token = token;
},
},
})
constant
constant/index.ts 用来存放定义的一些全局常量信息,暂时不是很多,所以统一放在了一个文件下。
调用store
import { useUiSetStore } from '@/store/modules/uiSettings'
const ui = useUiSetStore()
ui.login = true
or
store 是一个用 reactive 包装的对象,直接解构读取state会失去响应式,因此需要storeToRefs,它将为每一个响应式属性创建引用;需通过 .value 读取解构出来的值
<template>
<div>
<div>姓名:{{ userInfo.name }} 年龄:{{ userInfo.age }}</div>
<div>token:{{ token }}</div>
<div>getter值:{{ newName }}</div>
<button @click="handleUser">更新用户</button>
<button @click="handleAge">更新年龄</button>
<button @click="handleToken">更新token</button>
</div>
</template>
<script setup lang="ts">
import { storeToRefs } from 'pinia';
import { useUserStore } from '@/store/user'; //路径别名,引入store
const userStore = useUserStore();
//storeToRefs 会跳过所有的 action 属性
const { userInfo, token, newName } = storeToRefs(userStore);
//action 属性直接解构
const { updateUserInfo, updateAge, updateToken } = userStore;
const handleUser = () => {
updateUserInfo({ name: 'lisi', age: 24 });
};
const handleAge = () => {
//userInfo是一个ref响应式引用,需通过.value取值
updateAge(userInfo.value.age + 1);
};
const handleToken = () => {
updateToken('23234');
};
</script>
Store
Store是一个保存状态和业务逻辑的实体,可以自由读取和写入,并通过导入后在 setup 中使用
// pinia.ts
import { defineStore } from 'pinia';
// defineStore 调用后返回一个函数,调用该函数获得 Store 实体
export const useStore = defineStore({
// id: 必须的,在所有 Store 中唯一
id: 'App',
// state: 返回对象的函数
state: () => ({
count: 1
})
});
访问state
默认情况下,您可以通过 store
实例访问状态来直接读取和写入状态:
const store = useStore();
store.counter++;
相互引用
每个store可看做一个hook。相互引用即调用不同hook
import { defineStore } from 'pinia';
import { useUserStore } from './user';
enum Sex {
man = '男人',
woman = '女人',
}
export const userSexStore = defineStore('user2', {
state: () => {
return {
sex: Sex.man,
};
},
actions: {
updateSex() {
const userStore = useUserStore(); // 引用其他store
if (userStore.userInfo.name !== 'zhangsan') this.sex = Sex.woman;
},
},
});
路由钩子中使用store
使用store的前提是保证pinia已被注册
import { useUserStore } from '@/store/user';
router.beforeEach((to, from) => {
// 这样做是可行的,因为路由器是在其被安装之后开始导航的,
// 而此时 Pinia 也已经被安装。
const userStore = useUserStore();
if (!userStore.token && to.path !== "/login") {
return "/login";
}
});
重置状态
您可以通过调用 store 上的 $reset()
方法将状态 重置 到其初始值:
const store = useStore();
store.$reset();
改变状态
除了直接用 store.counter++
修改 store,你还可以调用 $patch
方法。 它允许您使用部分“state”对象同时应用多个更改:
1.第一种
store.$patch({
counter: store.counter + 1,
name: 'Abalam'
});
2.第二种
const handleClick = () => {
store.count++;
};
3.第三种方式 $patch 传递函数
const handleClickMethod = () => {
// 这里的state就是指代的仓库中的state
store.$patch((state) => {
state.count++;
state.helloWord = state.helloWord === "jspang" ? "Hello World" : "jspang";
});
};
4.第四种方式 action
当业务逻辑很复杂的时候,就将方法写在store
中的action
里
actions: {
changeState() {
this.count++
this.helloWord = 'jspang'
}
}
解构使用
解构后可以直接省略掉 store,减少了代码量
import { mainStore } from "../store/counter";
import { storeToRefs } from "pinia";
const store = mainStore();
// 进行解构
const { helloWord, count } = storeToRefs(store);
解构必须要用到storeToRefs()
函数!
替换state
您可以通过将其 $state
属性设置为新对象来替换 Store 的整个状态:
store.$state = { counter: 666, name: 'Paimon' };
您还可以通过更改 pinia
实例的 state
来替换应用程序的整个状态。 这在 SSR for hydration 期间使用。
pinia.state.value = {}
Getters
Getters 直接在 Store 上读取,形似 Store.xx,就和一般的属性读取一样,相当于 vue 中的计算方法
// 修改 store.js
import { defineStore } from "pinia";
import { otherState } from "@/store/otherState.js";
export const useStore = defineStore({
id: "myGlobalState",
state: ()=> ({
count: 2
}),
getters: {
// 一个基本的 Getter: 计算 count 的平方
// 使用参数
countPow2(state) {
return state.count ** 2;
},
// 获取其它 Getter, 直接通过 this
countPow2Getter() {
return this.countPow2;
}
// 使用其它 Store
otherStoreCount(state) {
// 这里是其他的 Store,调用获取 Store,就和在 setup 中一样
const otherStore = useOtherStore();
return otherStore.count;
},
}
});
// otherState.js
import { defineStore } from "pinia";
export const useStore = defineStore({
id: "otherState",
state: ()=> ({
count: 5
}),
});
actions
Pinia 没有 Mutations,统一在 actions 中操作 state,通过 this.xx 访问相应状态
虽然可以直接操作 Store,但还是推荐在 actions 中操作,保证状态不被意外改变
action 和普通的函数一样
action 同样可以像 Getter 一样访问其他的 Store,同上方式使用其它 Store,这里不在赘述,详细请移步 官方文档 Actions
action
// store.js
export const useStore({
state: ()=> ({
count: 2,
// ...
})
// ...
actinos: {
countPlusOne() {
this.count++;
},
countPlus(num) {
this.count += num;
}
}
})
可以自由扩展 官方文档 Plugins
异步 actions
import { defineStore } from 'pinia';
const getData = () => {
return new Promise<number>((resolve) => {
setTimeout(() => {
resolve(Math.random() * 100);
}, 200);
});
};
export const useListStore = defineStore('list', {
state: () => {
return {
list: [] as number[],
};
},
actions: {
async updateList() {
try {
const data = await getData();
this.list.push(data);
} catch {
/* empty */
}
},
},
});
数据持久化
由于pinia里没有自带的持久化存储,所以我们需要使用要持久化存储的插件 pinia-plugin-persistedstate npm地址:pinia-plugin-persistedstate 文档入口:pinia-plugin-persistedstate
npm i pinia-plugin-persistedstate
#或者使用 npm
yarn add pinia-plugin-persistedstate
基础使用
- 将插件添加到 pinia 实例上
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const store = createPinia()
store.use(piniaPluginPersistedstate) // 使用持久化插件
export default store
- 创建 Store 时,将
persist
选项设置为true
。
// user.ts
import { defineStore } from 'pinia'
// 你可以对 `defineStore()` 的返回值进行任意命名,但最好使用 store 的名字,同时以 `use` 开头且以 `Store` 结尾。(比如 `useUserStore`,`useCartStore`,`useProductStore`)
// 第一个参数是你的应用中 Store 的唯一 ID。
export const useUserInfoStore = defineStore('userInfo', {
// 其他配置...
// state: 返回对象的函数
state: () => {
return {
login: false
}
},
persist: true // 开始数据持久化
})
配置
默认配置如下
- 使用
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: {
// 在这里进行自定义配置
key: 'my-custom-key', //将被持久化存储在 localStorage 中的 my-custom-key key 中
storage: sessionStorage, //持久化存储在 sessionStorage中 ,默认localStorage
paths: ['save.me', 'saveMeToo'], // 只有 save.me 和 saveMeToo 被持久化,而未设置的不会被持久化。
},
})
问题点
-
pinia
里存储的用户信息,刷新后数据恢复默认 , 使用持久化插件解决 -
在axios中使用 ,报错未挂载
在响应拦截器 中调用 uiSettings = useUiSetStore()
-
在 js 或vue hooks中单独使用报错
错误
"getActivePinia()" was called but there was no active Pinia. Did you forget to install pinia? const pinia = createPinia() app.use(pinia) This will fail in production..
解决方式
-
为pinia单独新建一个文件store.js
-
然后在要使用pinia的js文件中导入
import { createPinia } from 'pinia' import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' const store = createPinia() store.use(piniaPluginPersistedstate) export default store
使用
import pinia from '@store/index' import { useUiSetStore } from '@/store/modules/uiSettings' const ui = useUiSetStore(pinia)
-