• 状态管理容器Pinia




    前言

    本文主要记录Vue3状态管理工具Pinia的介绍与使用


    一、集中式状态管理容器

    1、VueX

    集中式管理状态容器,可以实现任意组件之间的通讯

    核心:

    1. state 存储数据
    2. mutations 唯一修改数据
    3. actions 处理异步、处理业务
    4. getters 计算属性
    5. modules 模块式开发

    2、Pinia

    集中式管理状态容器,可以实现任意组件之间的通讯
    核心:

    1. state 存储数据
    2. actions 修改数据、处理异步、处理业务
    3. getters 计算属性

    优点:

    1. 完全支持TS,在JS中也提供自动补全
    2. 体积小
    3. 去除了 VeuX 的 mutations 和 modules
    4. actions 可同步也可异步
    5. 每个store都是独立的仓库,在打包时可拆分每个仓库
    6. 极致轻量化
    7. 自动加载store
    8. 支持Vue2和Vue3
    9. 可拓展性,可以通过本地存储等方式扩展PInia

    创建步骤:

    1. 安装Pinia
    npm install pinia -S
    
    • 1
    1. 建立大仓库

    这里有两种方式建立大仓库:

    • 直接在Mian.ts文件中建立
    import {createPinia,PiniaVuePlugin} from "pinia";
    let store = createPinia()
    app.use(store)
    
    • 1
    • 2
    • 3
    • 在其他文件下创建
    // index.ts
    import {createPinia,PiniaVuePlugin} from "pinia";
    /**
     * 利用createPinia方法创建大仓库(在Vue2中使用PiniaVuePlugin)
     * 并对外暴露该仓库
     * 在全局引入
     */
    let store = createPinia()
    export default store
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    全局引入:

    import store from "./store";
    app.use(store)
    
    • 1
    • 2

    在Vue3中使用createPiniaAPI, Vue2中使用PiniaVuePluginAPI

    1. 建立小仓库
    • 利用defineStoreAPI创建小仓库
      这里创建小仓库,必须传一个唯一名称,用来创建唯一的仓库,也就对应了他所说的模块化了吧。这里小仓库可以选择一个小仓库对应一个文件,也可以多个小仓库组成一个功能模块在一个文件中。
      这里也分为选择式和组合式两种

    选择式:

    /**
     * 选择式API仓库
     * defineStore方法定义小仓库,带两个参数
     *   1、仓库名称
     *   2、仓库配置对象
     * defineStore返回一个函数,让组件可以获取到仓库数据
     * 存储数据:state
     * 需对外暴露方法
     */
    import {defineStore} from "pinia";
    let userInfoStore = defineStore("info",{
        state:()=>{
            return {
                count: 99,
                arr: [1,2,3,4,5,6,7,8,9,10]
            }
        },
        actions: {
            //内部没有context上下文对象
            //没有commit、没有mutations去修改数据
            updateNum(a:number,b:number){
                this.count+=(a+b)
            }
        },
        getters: {
            total() {
                let result:number = this.arr.reduce((prev,next)=>{
                    return prev+next
                },0)
                return result
            }
        }
    })
    export default userInfoStore
    
    • 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
    • 34

    组合式:

    /**
     * 定义组合式API仓库
     * 务必返回一个对象:属性与方法可以提供给组件使用
     */
    import {defineStore} from "pinia";
    import {computed, reactive} from "vue";
    let userTodoStore = defineStore("todo",()=>{
    
        let todos = reactive([{id:1,title:'吃饭'},{id:2,title:'睡觉'}])
       let arr = reactive([1,2,3,4,5,6])
        const total = computed(()=>{
            return arr.reduce((prev,next)=>{
                return prev+next
            },0)
        })
        return {
            todos,
            total,
            updateTodo(){
                todos.push({id:3,title: '组合式API'})
            }
        }
    })
    export default userTodoStore
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    这里命名也可以通过枚举类来管理名称

    export const enum Names {
        TEST='TEST'
    }
    
    • 1
    • 2
    • 3

    使用:

    import {Names} from "../store-name";
    import {defineStore} from 'pinia'
    
    export const useTestStore = defineStore(Names.TEST,{
        state:()=>{
            return {
                current:1,
                name:'smz'
            }
        },
        getters:{
    
        },
        actions:{}
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    用法:

    1、选择式

    修改数据:

    • 使用返回的函数直接修改其属性
    import userInfoStore from "../../../store/modules/info";
    let infoStore = userInfoStore()
    infoStore.count++
    
    • 1
    • 2
    • 3
    • 使用返回函数上的$patch方法
    import userInfoStore from "../../../store/modules/info";
    let infoStore = userInfoStore()
    infoStore.$patch({count:222})
    
    • 1
    • 2
    • 3
    • 使用自定义方法,在actions中定义方法,可传参
    import userInfoStore from "../../../store/modules/info";
    let infoStore = userInfoStore()
    infoStore.updateNum(1,2)
    
    • 1
    • 2
    • 3

    仓库:

     actions: {
            //内部没有context上下文对象
            //没有commit、没有mutations去修改数据
            updateNum(a:number,b:number){
                this.count+=(a+b)
            }
        },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    注:在方法内部要用this,this指向仓库对象

    2、组合式

    修改数据:

    • 使用返回的函数直接修改其属性
    import userTodoStore from "../../../store/modules/todo";
    let todoStore = userTodoStore()
     todoStore.todos[0].title = '喝水'
    
    • 1
    • 2
    • 3
    • 使用computed计算属性,将计算值返回就能获取
      const total = computed(()=>{
                 return arr.reduce((prev,next)=>{
                    return prev+next
                 },0)
               })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 使用自定义方法,在return中定义方法,可传参
        updateTodo(){
                   todos.push({id:3,title: '组合式API'})
                }
    
    • 1
    • 2
    • 3

    注:在方法内部要用this,this指向仓库对象


    二、state

    修改值

    修改state中的数据有以下五种方式:
    修改值的方法:

    1. 直接修改
    Test.current++
    
    • 1
    1. 使用函数上自带的$patch方法
    Test.$patch({current:888,name:'smz2'})
    
    • 1
    1. 以$patch箭头函数形式
      这里的state就是仓库里的state
    Test.$patch((state)=>{state.current = 999})
    
    • 1

    4.使用函数上自带的$state方法

    该方法需要修改整个对象

     Test.$state = {current:2000,name: "smz3"} 
    
    • 1
    1. 在actions内定义方法,用this直接修改值
        actions:{setCurrent(number){this.current = number}}
        Test.setCurrent(28888)
    
    • 1
    • 2

    解构

    state解构

     const {current,name} = Test
    
    • 1

    这样解构出来的值是不具备数据响应式的
    需要使用 storeToRefs 包裹 和toRefs效果一致

    import {storeToRefs} from 'pinia'
    const {current,name} = storeToRefs(Test)
    
    • 1
    • 2

    三、actions - getters

    actions

    actions可以用同步也可以使用异步

    同步

    let result:User = {
        name: 'Pinia'
    }
    
    // actions内
     setUser(){
               this.user = result
            },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    异步

    const Login = ():Promise<User> =>{
        return new Promise(resolve => {
            setTimeout(()=>{
                resolve({
                    name:"1234"
                })
            },2000)
        })
    }
    
    // actions内
     async setUser2(){
               this.user =await Login()
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    相互调用

    actions内的方法是可以相互调用的

     setCurrent(number){
               this.current = number
           },
            setUser(){
               this.user = result
                this.setCurrent(666)
            },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    getters

    用来修饰值,这里有两种写法,在其中是可以相互调用的

      getters:{
            newName():string{
                return `name: ${this.name}`
            },
            newName2():string{
                return this.newName()
            }
        },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    四、API

    $reset

    重新初始化仓库,直接使用就可以将仓库初始化成原始值

    Test.$reset()
    
    • 1

    $subscribe

    响应 store 变化,该方法会监听state中的数据变化,与watch有相同的功能,比起普通的 watch(),使用 $subscribe() 的好处是 subscriptions 在 patch 后只触发一次
    可以设置detached:true 来控制组件被销毁时也能继续监听,还包括了其他一些配置项

    Test.$subscribe((args,state)=>{
      console.log(args)
      console.log(state)
    },{
      detached:true,
      deep:true,
      flush:'post'
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    $onAction

    监听action,在一个action即将被调用时,将触发该方法,回调接收一个对象, 其包含被调用 action 的所有相关信息:

    • store: 被调用的 store
    • name: action 的名称
    • args: 传递给 action 的参数

    除此之外,它会接收两个函数, 允许在 action 完成或失败时执行的回调。

    Test.$onAction(({ after, onError }) => {
     // 你可以在这里创建所有钩子之间的共享变量,
     // 同时设置侦听器并清理它们。
     after((resolvedValue) => {
       // 可以用来清理副作用 
       // `resolvedValue` 是 action 返回的值,
       // 如果是一个 Promise,它将是已经 resolved 的值
     })
     onError((error) => {
       // 可以用于向上传递错误
     })
    },true)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    它还会返回一个用来删除回调的函数。 请注意,当在组件内调用 store.$onAction() 时,除非 detached 被设置为 true, 否则当组件被卸载时,它将被自动清理掉。


    五、Pinia插件

    在Pinia中,数据在页面刷新后将被重新初始化,所以需要一个持久化插件来解决这个问题,原理都是将其存入localStorage中
    使用现成的持久化插件:

    pinia-plugin-persistedstate
    
    • 1
    • 安装:
    pnpm i pinia-plugin-persistedstate
    
    • 1
    • 挂载:
    import { createPinia } from 'pinia'
    import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
    
    const pinia = createPinia()
    pinia.use(piniaPluginPersistedstate)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 开启持久化
    const useTestStore = defineStore("Test",{
      // 开启数据持久化
      persist: true
    });
    
    • 1
    • 2
    • 3
    • 4

    一些其他配置:

    import { defineStore } from 'pinia'
    
    export const useStore = defineStore('main', s{
      state: () => {
        return {
          someState: 'hello pinia',
          nested: {
            data: 'nested pinia',
          },
        }
      },
      // 所有数据持久化
      // persist: true,
      // 持久化存储插件其他配置
      persist: {
        // 修改存储中使用的键名称,默认为当前 Store的 id
        key: 'storekey',
        // 修改为 sessionStorage,默认为 localStorage
        storage: window.sessionStorage,
        // 部分持久化状态的点符号路径数组,[]意味着没有状态被持久化(默认为undefined,持久化整个状态)
        paths: ['nested.data'],
      },
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    PInia持久化参考了文章:Pinia的使用以及数据持久化


    总结

    Pinia相对于VueX来说,简化了API的使用,支持TS,让学习成本大大降低。

  • 相关阅读:
    基于Python的算术编码的设计与实现
    前端架构师之08_JavaScript对象
    浏览器版本破百,“千年虫”问题再现?
    端到端自动驾驶系列(一):自动驾驶综述解析
    【c++每天一题】 字符串压缩
    【Vue3+Vite+Ts+element-plus】vue 使用 tsx语法详解
    第29章_瑞萨MCU零基础入门系列教程之改进型环形缓冲区
    网站一直被黑客攻击,我该如何反击
    笔记wife_assistant
    最大似然估计的介绍
  • 原文地址:https://blog.csdn.net/smznbhh/article/details/133255390