Advertisement

Vue3入门 - Pinia使用(替代Vuex)

阅读量:

pinia是Vue的存储库,它允许您跨组件/页面共享状态。它Vuex的替代品,其功能和性能更优于vuex,在后期使用中会逐渐被大家发现。

这里我们先使用pinia全局存储用户信息、接口访问令牌、菜单列表等数据,当然这些只是作为一个项目的基础部分,其他共享信息可根据你们的项目需求进行添加。

一、安装

在创建项目时,一般会有选项可以选择是否安装Pinia,选择是即可。如果创建项目里错过了也没关系,通过命令安装即可。代码如下:

复制代码
 // npm安装

    
 npm i pinia
    
 // yarn安装
    
 yarn add pinia
    
 // pnpm 安装
    
 pnpm add pinia
    
    
    
    

二、Pinia使用

2.1 定义数据类型

在Vue3+ts开中,对变量的定义和书写规则,则需要按ts语法进行编写,否则编辑器则会显示波浪线并提示您。

在项目的src/types目录下创建global.ts,用于存储类型数据。代码如下:

复制代码
 // 用户数据 接口

    
 export interface UserInfo {
    
   id?: number   // id
    
   username: string  // 用户名
    
   avatar?: string   // 头像
    
 }
    
 export type UserInfoType = UserInfo
    
  
    
 // 菜单信息 接口
    
 export interface MenuListInfo {
    
   id: number
    
   name: string,
    
   icon?: string,
    
   children: Array<MenuListInfo>
    
 }
    
 export type MenuListInfoType = MenuListInfo
    
    
    
    

2.2 创建global.ts

用户信息、访问令牌、菜单令牌在项目中使用频率较高,这里将这些信息放在global.js全局文件中。在src/stores目录中创建global.js,代码如下:

复制代码
 import { ref, computed, reactive } from 'vue'

    
 import { defineStore } from 'pinia'
    
 import type { UserInfoType, MenuListInfoType } from '@/types/global'
    
  
    
 // 用户信息
    
 export const useUserInfoStore = defineStore('userInfo', () => {
    
   // 定义用户信息状态(添加默认数据)
    
   let userInfo = reactive<UserInfoType>({username: '匿名'})
    
   // 定义getters获取用户信息响应数据
    
   const user_info = computed(() => userInfo)
    
   // 定义actions添加或修改用户信息函数
    
   const userInfoChange = (_value: UserInfoType) => userInfo = _value
    
   return { user_info, userInfoChange }
    
 })
    
  
    
 // 访问令牌
    
 export const useAccessTokenStore = defineStore('accessToken', () => {
    
   // 定义访问令牌状态
    
   const accessToken = ref<string>('')
    
   // 定义getters获取访问令牌响应式数据
    
   const access_token = computed(() => accessToken)
    
   // 定义actions添加或修改访问令牌
    
   const accessTokenChange = (_value: string) => accessToken.value = _value
    
   return { access_token, accessTokenChange }
    
 })
    
  
    
 // 菜单列表令牌
    
 export const useMenuListStore = defineStore('menuList', () => {
    
   // 定义菜单列表状态
    
   let menuList = reactive<Array<MenuListInfoType>>([])
    
   // 定义getters获取菜单数据
    
   const menu_list = computed(() => menuList)
    
   // 定义actions添加或修改菜单信息
    
   const menuListChange = (_value: Array<MenuListInfoType>) => menuList = _value
    
   return { menu_list, menuListChange }
    
 })
    
    
    
    

2.3 创建login.ts

当登录成功后,一般接口会同时返回用户信息和访问令牌,如果我们不希望引入useUserInfoStore后又引入useAccessTokenStore,使用一个useStore即可完成存储操作;此时可以在src/stores目录下再创建一个login.ts,专门用于处理登录相关数据。代码如下:

复制代码
 import { defineStore } from 'pinia'

    
 import type { UserInfoType } from '@/types/global'
    
 import { useUserInfoStore, useAccessTokenStore } from './global'
    
  
    
 // 登录统一处理用户信息和访问令牌
    
 export const useLoginStore = defineStore('login', () => {
    
   const { userInfoChange } = useUserInfoStore()
    
   const { accessTokenChange } = useAccessTokenStore()
    
   // 定义actions修改用户信息和访问令牌令牌
    
   const loginChange = (_userInfo: UserInfoType, _token: string) => {
    
     userInfoChange(_userInfo)
    
     accessTokenChange(_token)
    
   }
    
   return { loginChange }
    
 })
    
    
    
    

三、pinia持久化

在Vue2+vuex中,如果需要对数据持久化,则可使用vue-ls等插件,在actions的函数执行时,通过Vue.ls.set('名称', '值')将数据缓存到本地;而对于Pinia持久化操作,则更为方便,安装pinia-plugin-persistedstate插件后,只需要在defineStore配置下,即可自动将状态数据缓存到本地。

第一步:安装pinia-plugin-persistedstate(注:这里安装的是@4.0.0版本),代码如下:

复制代码
    pnpm i pinia-plugin-persistedstate
    

第二步:main.js安装插件,代码如下:

复制代码
 import { createApp } from 'vue'

    
 import { createPinia } from 'pinia'
    
 import persist from 'pinia-plugin-persistedstate'
    
 import App from './App.vue'
    
 import router from './router'
    
  
    
 const app = createApp(App)
    
 // 安装pinia-plugin-persistedstate
    
 app.use(createPinia().use(persist))
    
 app.use(router)
    
  
    
 app.mount('#app')
    
    
    
    

第三步:在defineStore的第三个参数上,添加persist: true,代码如下:

复制代码
 import { ref, computed } from 'vue'

    
 import { defineStore } from 'pinia'
    
 import type { UserInfoType, MenuListInfoType } from '@/types/global'
    
  
    
 // 用户信息
    
 export const useUserInfoStore = defineStore('userInfo', () => {
    
   // 定义用户信息状态(添加默认数据)
    
   const userInfo = ref<UserInfoType>({username: '匿名'})
    
   // 定义getters获取用户信息响应数据
    
   const user_info = computed(() => userInfo)
    
   // 定义actions添加或修改用户信息函数
    
   const userInfoChange = (_value: UserInfoType) => userInfo.value = _value
    
   return { userInfo, user_info, userInfoChange }
    
 }, {
    
   persist: true
    
 })
    
  
    
 // 访问令牌
    
 export const useAccessTokenStore = defineStore('accessToken', () => {
    
   // 定义访问令牌状态
    
   const accessToken = ref<string>('')
    
   // 定义getters获取访问令牌响应式数据
    
   const access_token = computed(() => accessToken)
    
   // 定义actions添加或修改访问令牌
    
   const accessTokenChange = (_value: string) => accessToken.value = _value
    
   return { accessToken, access_token, accessTokenChange }
    
 }, {
    
   persist: true
    
 })
    
  
    
 // 菜单列表令牌
    
 export const useMenuListStore = defineStore('menuList', () => {
    
   // 定义菜单列表状态
    
   const menuList = ref<Array<MenuListInfoType>>([])
    
   // 定义getters获取菜单数据
    
   const menu_list = computed(() => menuList)
    
   // 定义actions添加或修改菜单信息
    
   const menuListChange = (_value: Array<MenuListInfoType>) => menuList.value = _value
    
   return { menuList, menu_list, menuListChange }
    
 }, {
    
   persist: true
    
 })
    
    
    
    

此时在页面中调用useLoginStore ,数据将会自动缓存到本地存储中,调用代码如下:

复制代码
 <script setup lang="ts">

    
 import { useLoginStore } from '@/stores/login'
    
 const login = useLoginStore()
    
 // 演示延迟响应数据
    
 setTimeout(() => {
    
   login.loginChange({username: 'tome'}, "test1234")
    
 }, 2000);
    
 </script>
    
    
    
    

如下图:

注意:细心朋友可能已发现了,在pinia持久化第三步中,代码已悄然有所改变。这是因为必须要作相应调整,否则就算配置了persist: true,也无法缓存状态数据。需要注意以下几点:

  1. 定义状态数据时,不要使用reactive,改为ref来定义,这里因pinia-plugin-persistedstate无法缓存reactive响应数据。
  2. 另外必须要注意的是,如果你希望缓存userInfo、accessToken这些状态数据,必须在return中返回这些状态变量,否则无法缓存。
  3. 当stores中某个defineStore中配置persist:后,如未生效,可重启服务后查看!

对于persist来说,还有更多参数可供设置,如下表:

序号 名称 类型 描述
1 key type: string default: store.$id 用于引用存储中存储的反序列化数据。
2 storage type: StorageLike default: localStorage 将数据持久化到的存储。必须有getItem: (key: string) => string null和setItem: (key: string, value: string) => void方法。
3 serializer type: Serializer default: JSON.stringify / destr 自定义序列化器在持久化之前序列化数据,并在重新填充存储之前反序列化数据。必须有serialize: (value: StateTree) => string和deserialize: (value: string) => StateTree方法。
4 pick type: string[] Path[] default: undefined 点表示法路径数组,用于选择应该持久化的内容。[]表示不保留状态,undefined表示保留整个状态。
5 omit type: string[] Path[] default: undefined 要从应该持久化的内容中省略的点表示法路径数组。[]或undefined表示整个状态保持不变(没有遗漏)。

| 6| beforeHydrate| type: (context: PiniaPluginContext) => void
default: undefined| 钩子函数在用持久化数据填充存储状态之前运行。这个钩子可以访问整个PiniaPluginContext。这可以用来在缓存之前强制执行特定的动作。 |
| 7| afterHydrate| type: (context: PiniaPluginContext) => void
default: undefined| 在重新激活持久状态后运行钩子函数。这个钩子可以访问整个PiniaPluginContext。这可以用来加强缓存后的具体行动。 |
| 8| debug| type: boolean
default: false| 当设置为true时,在持久化/水化存储时可能发生的任何错误都将使用console.error记录。 |

3.1 key用法

复制代码
 import { defineStore } from 'pinia'

    
 export const useStore = defineStore('store', {
    
   state: () => ({
    
     someState: 'hello pinia',
    
   }),
    
   persist: {
    
     key: 'my-custom-key',
    
   },
    
 })
    
    
    
    

3.2 storage用法

复制代码
 import { defineStore } from 'pinia'

    
 export const useStore = defineStore('store', {
    
   state: () => ({
    
     someState: 'hello pinia',
    
   }),
    
   persist: {
    
     storage: sessionStorage,
    
   },
    
 })
    
    
    
    

3.3 serializer用法

复制代码
 import { defineStore } from 'pinia'

    
 import { parse, stringify } from 'zipson'
    
 export const useStore = defineStore('store', {
    
   state: () => ({
    
     someState: 'hello pinia',
    
   }),
    
   persist: {
    
     serializer: {
    
       deserialize: parse,
    
       serialize: stringify
    
     }
    
   },
    
 })
    
    
    
    

3.4 pick用法

复制代码
 import { defineStore } from 'pinia'

    
 export const useStore = defineStore('store', {
    
   state: () => ({
    
     save: {
    
       me: 'saved',
    
       notMe: 'not-saved',
    
     },
    
     saveMeToo: 'saved',
    
   }),
    
   persist: {
    
     pick: ['save.me', 'saveMeToo'],
    
   },
    
 })
    
    
    
    

3.5 omit用法

复制代码
 import { defineStore } from 'pinia'

    
 export const useStore = defineStore('store', {
    
   state: () => ({
    
     ignore: {
    
       me: 'not-saved',
    
       notMe: 'saved',
    
     },
    
     ignoreMeToo: 'not-saved',
    
   }),
    
   persist: {
    
     omit: ['ignore.me', 'ignoreMeToo'],
    
   },
    
 })
    
    
    
    

3.6 beforeHydrate用法

复制代码
 import { defineStore } from 'pinia'

    
 export const useStore = defineStore('store', {
    
   state: () => ({
    
     someState: 'hello pinia',
    
   }),
    
   persist: {
    
     beforeHydrate: (ctx) => {
    
       console.log(`about to hydrate '${ctx.store.$id}'`)
    
     }
    
   },
    
 })
    
    
    
    

3.7 afterHydrate用法

复制代码
 import { defineStore } from 'pinia'

    
 export const useStore = defineStore('store', {
    
   state: () => ({
    
     someState: 'hello pinia',
    
   }),
    
   persist: {
    
     afterHydrate: (ctx) => {
    
       console.log(`just hydrated '${ctx.store.$id}'`)
    
     }
    
   },
    
 })
    
    
    
    

另外,需要了解更多相关信息,可去官网查看:Configuration | Pinia Plugin Persistedstate

四、key添加前缀

在项目开发中,经常会看到很多本地缓存,都带有统一的前缀,这是怎么做到的呢?从目前来看,如果自定义缓存key值,可以在开始persist位置自定义key,但是否可以统一修改呢?当然也是可以的,代码如下:

复制代码
 import { createApp } from 'vue'

    
 import { createPinia } from 'pinia'
    
 import { createPersistedState } from 'pinia-plugin-persistedstate'
    
 import App from './App.vue'
    
  
    
 const app = createApp(App)
    
 const pinia = createPinia()
    
 pinia.use(createPersistedState({
    
     debug: true,
    
     key: (value) => "Test_" + value
    
 }))
    
 app.use(pinia)
    
  
    
 app.mount('#app')
    
    
    
    

重启服务后,再看缓存数据,key值则统一被添加了前缀。如下图:

六、Pinia的改造

示例代码:

复制代码
 <script setup lang="ts">

    
 import { useLoginStore } from '@/stores/login'
    
 const login = useLoginStore()
    
 // 演示延迟响应数据
    
 setTimeout(() => {
    
   login.loginChange({username: 'tome'}, "test1234")
    
 }, 2000);
    
 </script>
    
    
    
    

如代码可见,后期如果useStore定义越来越多,引用地址会更为复杂,如何统一引入同一个地址,就可以读取到所有useStore呢?这也是可以实现的,首先将目录结构调整如下图:

后期所有子模块store可以定义在modules目录中,并且stores目录下所有useStore通过stores/index.ts统一导出。

首先,将main.ts中定义的pinia部分,移到stores/index.ts中,代码如下:

复制代码
 import { createPinia } from 'pinia'

    
 import { createPersistedState } from 'pinia-plugin-persistedstate'
    
 const pinia = createPinia()
    
 pinia.use(createPersistedState({
    
     debug: true,
    
     key: (value) => "Test_" + value
    
 }))
    
 export default pinia
    
 // 使用通配符将引入地址(modules/login)中非default的全部导出
    
 export * from './global'
    
 export * from './modules/login'
    
    
    
    

然后在main.ts中引入@/stores,将pinia安装到应用到,代码如下:

复制代码
 import { createApp } from 'vue'

    
 import pinia from '@/stores'
    
 import App from './App.vue'
    
  
    
 const app = createApp(App)
    
 app.use(pinia)
    
  
    
 app.mount('#app')
    
    
    
    

此时,我们就可以直接通过@/store/index引入,直接读取到useLoginStore 了,后期不管定义多少useStore,都可以通过该统一地址获取。代码如下:

复制代码
 <script setup lang="ts">

    
 import { useLoginStore } from '@/stores/index'
    
 const { loginChange } = useLoginStore()
    
  
    
 setTimeout(() => {
    
   loginChange({username: 'tome'}, "test1234")
    
 }, 2000);
    
 </script>
    
    
    
    

到这里,Pinia的安装、使用、持久化、key添加前缀、统一输出等已全部讲完,希望对大家有所帮助。

全部评论 (0)

还没有任何评论哟~