Pinia状态管理

238 阅读4分钟

什么是 Pinia

如果你之前使用过 vuex 进行状态管理的话,那么 pinia 就是一个类似的插件。它是最新一代的轻量级状态管理插件。按照尤雨溪的说法,vuex 将不再接受新的功能,建议将 Pinia 用于新的项目。业界将其等同于最新vuex5.x版本(vuex4.x升级版本)

Pinia 优点

  • Pinia 没有 Mutations

  • Actions 支持同步和异步

  • 没有模块的嵌套结构

    Pinia 通过设计提供扁平结构,就是说每个 store 都是互相独立的,谁也不属于谁,也就是扁平化了,更好的代码分割且没有命名空间。 当然你也可以通过在一个模块中导入另一个模块来隐式嵌套 store,甚至可以拥有 store 的循环依赖关系

  • 更好的 TypeScript 支持

    不需要再创建自定义的复杂包装器来支持 TypeScript 所有内容都类型化, 并且 API 的设计方式也尽可能的使用 TS 类型推断

  • 不需要注入、导入函数、调用它们,享受自动补全,让我们开发更加方便

  • 无需手动添加 store,它的模块默认情况下创建就自动注册的

  • Vue2 和 Vue3 都支持

  • 支持 Vue DevTools

Pinia 和 Vuex

Vuex: state 、Gettes 、 Mutations(同步)、Actions(异步)

Pinia:state .Gettes 、Actions(同步异步都支持)

Vuex当前最新版是4.x

  • Vuex4用于Vue3
  • Vuex3用于Vue2

Pinia当前最新版是2.x

  • 即支持Vue2也支持Vue3

图像.png

使用 Pinia

安装

yarn add pinia
# 或者使用 npm
npm install pinia

创建stores文件夹 index.ts

import { createPinia } from 'pinia'
//引入持久化插件
import piniaPluginPersist from 'pinia-plugin-persist'
const store = createPinia()
//使用存储插件
store.use(piniaPluginPersist)

export default store

main.ts 集成store

import { createApp } from 'vue'
import router from './router'
import store from '@/stores'

createApp(App).use(router).use(store).mount('#app')

持久化存储

  1. 安装持久化存储插件
  2. 集成插件
  3. 使用插件
//引入持久化插件
import piniaPluginPersist from 'pinia-plugin-persist'
//集成持久化存储插件
storeRoot.use(piniaPluginPersist)
//使用该插件,开启数据缓存
persist: {
    //这里存储默认使用的是session
    enabled: true,
    strategies: [
        {
            // key的名称
            key: 'userKey',
            //更改默认存储,我更改为localstorage
            storage: localstorage,
            //可以选择哪些进入local存储,这样就不用全部都过
            //默认是全部进去存存储进去
            paths: ['list'],
        },
    ],
},

定义模块与组件使用

stores 文件夹定义一个 store

Store 是使用 defineStore() 定义的,并且它需要一个唯一名称,作为第一个参数传递。defineStore 方法定义 store 返回一个函数第一个参数是应用程序中 store 的唯一 id。Pinia 使用用它将 store 连接到 devtools 第二个参数为选项对象,定义 state, actions,gettters, persist

import { defineStore } from 'pinia'

export const cartStore = defineStore('cart', {
    state: () => {
        return {
            list: [
                { id: 1, name: 'JavaScript高级编程', price: 78.98 },
                { id: 2, name: 'Vue高级编程', price: 88.88 },
            ],
        }
    },
    actions: {},
    getters: {},
    persost: {},
})

组件使用

import { cartStore } from '@/Store/cart'

const { list, addProduct } = cartSrore()

注意:

  1. 解构出来的状态失去响应式
  2. getters和state定义的名称不能相同

state 操作

state 是 store 的核心部分。 我们通常从定义应用程序的状态开始。在 Pinia 中,状态被定义为返回初始状态的函数

import { defineStore ] from "pinia"

const useStore = defineStore('storeld', {
    //推荐使用完整炎型推断的箭头函数
    state: () => {
        return {
            //所有这些医性都将自动推断其类型
            counter: 0,
            name: "Eduardo",
            isAdmin: true,
        }
    },
})

访问 “state”

默认情况下,您可以通过store实例访问状态来直接读取和写入状态:

const store = useStore()
store. counter-

重置状态

您可以通过调用store 上的 $reset() 方法将状态重置到其初始值:

const store = useStore()
store.$reset()

改变状态

除了直接用store.counter++修改store,你还可以调用$patch方法。它允许您使用部分"state"对象同时应用多个更改:

store.$patch({
    counter: store.counter + 1,
    name: 'Abalam',
})

但是,使用这种语法应用某些突变非常困难或代价高昂:任何集合修改(例如,从数组中推送、删除、拼接元素)都需要您创建一个新集合。正因为如此,$patch方法也接受一个函数来批量修改集合内部分对象的情况:

cartStore.$patch((state) => {
    state.items.push({ name: 'shoes', quantity: 1})
    state.hasChanged = true
))

替换state

可以通过将其 $state 属性设置为新对象来替换 Store 的整个状态:

store.$state = { counter: 666, name: 'Paimon' ]

还可以通过更改 pinia 实例的state来替换应用程序的整个状态。这在SSR for hydration期间使用。

pinia.state.value = {}

订阅状态

可以通过store的 $subscribe()方法查看状态及其变化,类似于Vuex的 subscribe方法。与常规的watch()相比,使用$subscribe()的优点是subscriptions只会在patches 之后触发一次(例如,当使用上面的函数版本时)。

cartstore.$subscribe(( mutation,state) => {
    //import{ MutationType } from 'pinia'
    mutation.type // 'direct' / 'patch object' / 'patch function'
    //与cartStore.$id相同
    mutation.storeId // 'cart'
    //适用于 mutation.type = 'patch object '
    mutation.payload//补了对象传逃给to cartStore.$patch()
    //每当它发生变化时,将整个状态持久化到本地存储
    localStorage.setItem( 'cart', JSON.stringify(state))
})

默认情况下,staote subscriptions 绑定到添加它们的组件(如果store位于组件的 sotup()中),意思是,当组件被卸载时,它们将被自动删除。如果要在卸载组件后保留它们,请将{detached:true}作为第二个参数传递给detach当前组件的state subscription:

export default {
    setup({
        const somestore = useSomestore()
        
        //此订阅将在组件卸戟后保密
        someStore.$subscribe(callback, { detached: true }
        
        //...
    },
)

Getters

Getter 完全等同于 Store 状态的 计算值

Getter完全等同于Store状态的计算值。它们可以用defineStore()中的getters属性定义。他们接收"状态"作为第一个参数以鼓励箭头函数的使用:

export const usestore = definestore('main', {
    state: () => {
        counter: 0,
    }),
    getters: {
        doubleCount: (state) => state.counter * 2.
    },
})

将参数传递给 getter

Getters 只是幕后的computed属性,因此无法向它们传递任何参数。但是,您可以从getter返回一个函数以接受任何参数:

export const useStore = defineStore( 'main', {
    getters: {
        getUserById: (state) => {
            return (userld) => state.users.find((user) => user.id = userId)
        },
    },
})

并在组件中使用:

<template>
    <p>User 2: {{ getUserBy1d(2) }}</P>
</template>

<script>
    export default {
        setup() {
            const store = useStore()
            
            return { getUlserById: store.getUserById }
        },
</script>

Actions

Actions 相当于组件中的 methods。 它们可以使用 defineStore() 中的 actions 属性定义,并且它们非常适合定义业务逻辑:

export const useStore = defineStore( 'main', ({
    state: () => {
        counter: 0,
    }),
    actions: {
        increment () {
            this.counter++
        },
        randomi zecounter() {
            this.counter = Math.round(100 * Math.random())
        },
    },
})