前言

本文主要记录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. 建立大仓库

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

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

全局引入:

import store from "./store";
app.use(store)

在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

组合式:

/**
 * 定义组合式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

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

export const enum Names {
    TEST='TEST'
}

使用:

import {Names} from "../store-name";
import {defineStore} from 'pinia'

export const useTestStore = defineStore(Names.TEST,{
    state:()=>{
        return {
            current:1,
            name:'smz'
        }
    },
    getters:{

    },
    actions:{}
})

用法:

1、选择式

修改数据:

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

仓库:

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

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

2、组合式

修改数据:

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

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


二、state

修改值

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

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

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

该方法需要修改整个对象

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

解构

state解构

 const {current,name} = Test

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

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

三、actions - getters

actions

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

同步

let result:User = {
    name: 'Pinia'
}

// actions内
 setUser(){
           this.user = result
        },

异步

const Login = ():Promise<User> =>{
    return new Promise(resolve => {
        setTimeout(()=>{
            resolve({
                name:"1234"
            })
        },2000)
    })
}

// actions内
 async setUser2(){
           this.user =await Login()
        }

相互调用

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

 setCurrent(number){
           this.current = number
       },
        setUser(){
           this.user = result
            this.setCurrent(666)
        },

getters

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

  getters:{
        newName():string{
            return `name: ${this.name}`
        },
        newName2():string{
            return this.newName()
        }
    },

四、API

$reset

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

Test.$reset()

$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'
})

$onAction

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

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

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

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

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


五、Pinia插件

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

pinia-plugin-persistedstate
  • 安装:
pnpm i pinia-plugin-persistedstate
  • 挂载:
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'

const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
  • 开启持久化
const useTestStore = defineStore("Test",{
  // 开启数据持久化
  persist: true
});

一些其他配置:

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'],
  },
})

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


总结

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

Logo

前往低代码交流专区

更多推荐