• 【Pinia】小菠萝详细使用说明


    1. 介绍

    1.1 Pinia介绍

    在这里插入图片描述
    官网地址:https://pinia.vuejs.org/zh/

    主要优点:
    全局状态管理工具,Pinia.js有以下的特点:

    • 完整的ts支持
    • 体积小,只有1kb左右
    • 去除mutations,只有state,getter,action
    • actions支持同步与异步
    • 代码扁平化没有模块嵌套,只有store的概念,store之间可以自由使用,每个store都是独立的。
    • 无需手动添加store,store一旦创建便会自动添加
    • 支持 vue2和vue3
    • 支持插件扩展功能

    什么时候可以使用?
    保存全局信息的时候,例如登录的个人信息。
    复杂的表单时,比如多个步骤的表单。

    1.2 pinia的属性说明

    defineStore方法的第一个参数:相当于为容器起一个名字。注意:这里的名字必须唯一,不能重复。
    defineStore方法的第二个参数:可以简单理解为一个配置对象,里边是对容器仓库的配置。当然这种说明是以对象的形式存在。
    state属性: 用来存储全局的状态的属性。
    getters属性:用来监视或者说是计算状态的变化的,有缓存的功能。
    actions属性:用来修改state全局状态数据的,复杂的计算逻辑可以放到这里。

    2. 安装

    pnpm i pinia
    
    • 1

    在main.ts中添加

    import {createPinia} from 'pinia'
    
    const store = createPinia()
    app.use(store)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3. 初步使用

    (1)在项目的src目录下新建store文件夹
    (2)在store文件夹里面新建store-name.ts和index.ts。

    store-name用来定义枚举类型的名称作为store的唯一值。
    store-name.ts

    #这里定义了2个枚举名称分别为USERSYSTEM
    export const enum Names{
      USER = "USER",
      SYSTEM = "SYSTEM"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在index.ts中,
    useUserStore是通过选项式类型Store
    useSystemStore是组合式类似的Store

    引入仓库插件:

    import {defineStore} from 'pinia'
    
    • 1

    定义仓库1,指定的唯一id,是在使用时需要
    这里的useXXXXXStore 是约定俗成的命名规范,下面就是选项式定义仓库的方式。

    export const useXXXXXStore = defineStore("唯一id",{
      state:()=>{},
      getters:{},
      actions:{}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    以下两种分别介绍了选项式和组合式仓库定义的具体应用,针对唯一id,这里抽取出来作为独立的枚举类型。
    也可以不用抽取,直接写,但是必须所定义的字符串是唯一的:

    import {defineStore} from 'pinia'
    import { Names } from './store-name'
    import {ref} from "vue"
    
    //options 选项式API
    export const useUserStore = defineStore(Names.USER,{
      //data: 类似于组件中的data
      state:()=>{
        return {
          name: "张三",
          age:20
        }
      },
      //computed 修饰一些值
      getters:{
      },
      //methods 可以做同步、异步,提交state
      actions:{
      }
    })
    
    //setup store 组合式API
    export const useSystemStore = defineStore(Names.SYSTEM,()=>{
      const systemInfo = ref({version:"win10"})
      function getMemory(){
        return "16G"
      }
      return {systemInfo, getMemory}
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    在vue中,分别调用2种不同store库,都可以使用:

    <script setup lang='ts'>
    import { useUserStore, useSystemStore } from "./store"
    const user = useUserStore()
    const system = useSystemStore()
    </script>
    
    <template>
      <div>
        <p>User Name:{{ user.name }}</p>
        <p>User Age: {{ user.age }}</p>
      </div>
    
      <div>
        <p>System Info: {{ system.systemInfo.version }}</p>
        <p>System Memory: {{ system.getMemory() }}</p>
      </div>
    </template>
    
    <style scoped></style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    页面显示如下:
    在这里插入图片描述

    4. store具体使用

    4.1 值修改

    4.2.1 直接修改

    通过点击changeName和changeAge事件,可以直接修改用户的信息:
    在这里插入图片描述

    在点击按钮时,对应的pinia中保存的对象的值也发生改变。
    在这里插入图片描述

    4.2.2 通过$patch整体修改

    //整体修改
    const changeNameAndAge = () =>{
      user.$patch({
        name: names[randomInt(0, 4)],
        age: user.age + 1
      })
      console.log(user.name,user.age)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    在这里插入图片描述

    4.2.3 通过$patch函数式

    user.$patch((state) => {    
        state.age = randomInt(1,100)
        state.name = checkName(state.age)
      })
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

    4.2.4 通过$state整体修改

    这种方式通常需要将state中的对象全部写上,否则会报错,所以一般不推荐使用。

      user.$state = {
        name: "法外狂徒张三",
        age: 30
      }
    
    • 1
    • 2
    • 3
    • 4

    4.2.5 通过actions修改

    修改src/store/index.ts中useUserStore中actions:
    在这里插入图片描述

    然后在vue端调用:

    const changeNameAndAge4 = ()=>{
      user.setNameAndAge("张三",30)
    }
    
    • 1
    • 2
    • 3

    4.2 解构store

    直接解构Store中的对象时,是不具有响应式的,参考下图:
    在这里插入图片描述

    因此,为了具有响应式,需要添加pinia指定的组件storeToRefs。
    第一步,先导入组件

    import {storeToRefs} from "pinia"
    
    • 1

    第二步:在要解构的时候,将store对象包裹起来

    const {name,age} = storeToRefs(user)
    
    • 1

    5 actions使用

    模拟异步登录获取student信息

    第一步,在store中定义异步方法getStudent

    //定义数据类型
    type Student = {
      name: string,
      grade: number
    }
    
    //模拟登陆时,2秒后获取到student的值
    const loginUser = (): Promise => {
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve({
            name: "小明",
            grade: 3
          })
        }, 2000)
      })
    }
    
    
    // 在actions中使用异步获取loginUser的数据
    export const useStudentStore = defineStore("student", {
      state: () => {
        return {
          student: {}
        }
      },
      getters: {},
      actions: {
        async getStudent() {
          this.student = await loginUser()
        }
      }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    异步查询时,通常都是async和await搭配一起使用的。

    第二步,在vue中使用:

    //student
    const studentStore = useStudentStore()
    const getStudentInfo = () => {
      studentStore.getStudent()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    studentStore可以直接调用actions里面的方法getStudent()

    除此之外,在actions中,方法也可以互相调用。

    // actions中方法互调
    // 当name参数有值时,就更新,否则使用登陆查询的值
    export const useStudentStore = defineStore("student", {
      state: () => {
        return {
          student: {}
        }
      },
      getters: {},
      actions: {
        async getStudent(name:string|null) {
          this.student = await loginUser()
          this.updateStudent(name)
        },
        updateStudent(name:string|null){
          if(name){
            this.student.name = name
          }
        }
      }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    6. getters使用

    6.1 通过this获取

        getFullName():string {
          return `${this.student.name}大学${this.student.grade}年级`
        },
    
    • 1
    • 2
    • 3

    6.2 通过state获取

    get的每个方法中都带有一个默认的state的参数,可以直接使用state来获取值

        getFullName2(state){
          return state.student.name + "大学" + state.student.grade + "年级"
        },
    
    • 1
    • 2
    • 3

    6.3 传参使用

    由于默认的参数是state,所以要想传参,需要返回一个带参数的方法提供调用就可以。
    这里需要写成=>形式。

        getFullName3: (state)=>{
          return (firstName:string)=>  firstName +"-" + state.student.name
        }
    
    • 1
    • 2
    • 3

    在vue中使用时,可以传入对应的firstName变量值。

    student fullName3: {{ studentStore.getFullName3("张姓:") }}

    • 1

    7.API

    7.1 重置state($reset)

    重置state为初始化的状态:

    const resetStudentInfo = () => studentStore.$reset()
    
    • 1

    7.2 $subscribe

    当state里面的值被修改时,就会触发该事件,
    所以当需要监听新旧的值时,可以在此添加一些对应的逻辑判断。

    studentStore.$subscribe((args,state)=>{
      console.log("args===>",args)
      console.log("state===>",state)
    })
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

    7.3 $onActon

    本身onAction是在调用到actions中的事件时触发。

    studentStore.$onAction((args)=>{
      args.after(()=>{
        console.log("after")
      })
      console.log("onAction args===>",args)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    但是也可以做更精细的控制,就是在actions中的某个事件执行完之后再触发。
    在这里插入图片描述

    比如上面的1,2先分别调用了actions中的setStudent,updateStudent事件。
    然后,再分别调用了args.after这个方法。
    这种可以用来处理在某个actions动作发生后,添加一些处理逻辑。

    8. 持久化插件

    8.1 插件安装

    state中的值,在页面进行刷新后,会丢失修改的数据,所以需要借助了浏览器的存储来持久化。
    安装插件

    pnpm i pinia-plugin-persistedstate
    
    • 1

    在main.ts中引入插件

    import { createApp } from 'vue'
    import './style.css'
    import App from './App.vue'
    import {createPinia} from 'pinia'
    import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'  //引入
    
    let app = createApp(App)
    const pinia = createPinia()
    app.use(pinia)
    pinia.use(piniaPluginPersistedstate) //使用
    
    app.mount('#app')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    8.2 使用

    第一种,简单使用,直接指定开启

    export const useUserStore = defineStore(Names.USER, {
      state: () => {},
      getters: {},
      actions: {},
      persist: true //持久化开启
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    第二种: 指定自定义key

    export const useUserStore = defineStore(Names.USER, {
      state: () => {},
      getters: {},
      actions: {},
      persist: {
        key: "my-custom-key" //自定义key
      }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    第三种:组合式API方式的持久化,以及制定的存储类型

    export const useSystemStore = defineStore(Names.SYSTEM, () => {
      const systemInfo = ref({ version: "win10" })
      function getMemory() {
        return "16G"
      }
      return { systemInfo, getMemory }
    },
      {  //持久化,并自定义key
        persist: {
          key: "my-custom-key",
          storage: sessionStorage,  //sessionStorage,localStorage,
        }
      }
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    8.3 局限性

    8.3.1 引用类型可能会失效

    下面这种情况,b是对a的引用。因此,b和a都是指向同一个地址。

    const a = {
      1: 'one',
      2: 'two',
      // ...
    }
    const b = a
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    序列化之前,a===b 结果是true

    在持久化之后,由于数据将会被序列化,因此引用在刷新时将会丢失。

    再次反序列化之后,a和b有着相同的内容,但是指向的是不同的对象。
    a === b 结果为false

    解决方法:
    采取避免 a 或 b 被持久化的方法(使用 paths 选项),
    并使用 afterRestore 钩子在恢复数据后重新存储它们。
    这样 a 和 b 就又会有着相同的引用,两者之间的联系就恢复了。

    8.3.2 基本数据类型之外的不会被序列化

    解决方法:
    使用 afterRestore 钩子在恢复数据后重新创建对象。
    使用自定义的 serializer 来配置你想要持久化的数据类型。

    8.4 全局配置

    使用createPersistedState来进行全局配置。

    import { createApp } from 'vue'
    import './style.css'
    import App from './App.vue'
    import {createPinia} from 'pinia'
    // import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
    import { createPersistedState } from 'pinia-plugin-persistedstate'
    
    let app = createApp(App)
    const pinia = createPinia()
    app.use(pinia)
    // pinia.use(piniaPluginPersistedstate)
    
    pinia.use(
      createPersistedState({
        storage: sessionStorage,
        key: id => `__persisted__${id}`,
        auto: true,
      })
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    全局配置以后,可以不需要在每个store中再额外配置,但是可以指定的store中禁用持久化。

    import { defineStore } from 'pinia'
    defineStore('not-persisted', {
      state: () => ({ saved: '' }),
      persist: false, //显示指定某个store不持久化
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5

    也可以为单独的state中变量分别指定持久化方式:

    import { defineStore } from 'pinia'
    defineStore('store', {
      state: () => ({
        toLocal: '',
        toSession: '',
        toNowhere: '',
      }),
      persist: [
        {
          paths: ['toLocal'],
          storage: localStorage,
        },
        {
          paths: ['toSession'],
          storage: sessionStorage,
        },
      ],
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
  • 相关阅读:
    Java获取dbcp连接池
    在vite(vue)项目中使用mockjs
    js逆向-F12及网页重定向检测
    【无标题】
    事件研究法与其应用(2)---Excel实操步骤
    创投课程研报专题课 | 如何写出高质量研报
    pycharm如何安装pygame库
    第八周周报
    【算法 | 位运算No.2】leetcode 371. 两整数之和
    HCIA11 网络安全之本地 AAA 配置实验
  • 原文地址:https://blog.csdn.net/wang6733284/article/details/133623411