likes
comments
collection
share

Pinia 知识点大梳理,结尾带你做一个属于自己的插件~

作者站长头像
站长
· 阅读数 42

为什么需要状态管理

  • Web 应用程序变得越来越复杂
  • 组件和页面较多,在传递/更新数据时候很大概率会出错(数据流复杂)

Pinia 是什么?

官方文档介绍:pinia.vuejs.org/zh/introduc…

Pinia (发音为 /piːnjʌ/,类似英文中的 “peenya”) 是最接近有效包名 piña (西班牙语中的 pineapple,即“菠萝”) 的词。 菠萝花实际上是一组各自独立的花朵,它们结合在一起,由此形成一个多重的水果。 与 Store 类似,每一个都是独立诞生的,但最终它们都是相互联系的。 它(菠萝)也是一种原产于南美洲的美味热带水果。 -- 官方文档

一个 Store 应该包含可以在整个应用中访问的数据。这包括在许多地方使用的数据,例如显示在导航栏中的用户信息,以及需要通过页面保存的数据,例如一个非常复杂的多步骤表单。 -- 官方文档

Pinia 知识点大梳理,结尾带你做一个属于自己的插件~

Vue 中的状态管理

Vuex

  • 学习成本高,比较复杂,难以理解
  • 模块化管理不够直观,需要额外的学习成本
  • 对 TS 支持不太好
  • 存在性能问题

Pinia 知识点大梳理,结尾带你做一个属于自己的插件~

Pinia

  • 学习成本较低,易于理解,更加直观和简洁
  • 更好的 TypeScript 支持,添加 TS 更加容易
  • 体积很小,只有 1KB 左右
  • 支持多个 Store,没有 Vuex 复杂的模块化

你仍然可以通过导入和使用另一个 Store 来隐含地嵌套 stores 空间。虽然 Pinia 从设计上提供的是一个扁平的结构,但仍然能够在 Store 之间进行交叉组合。你甚至可以让 Stores 有循环依赖关系。 -- 官方文档

核心概念

  • Store 整个仓库
  • State (Data) 数据状态
  • Action (Methods) 方法
  • Getter (Computed) 计算值

Store

  • Store 是一切东西的 “外壳“ ,Store 中包含了 State, Action, Getter
  • pinia 中 store 各自独立,甚至可以交叉使用
  • 另外注意的是,定义 Store 时,传入的 ID 要唯一

两种写法

 // Option Store
 export const useCounterStore = defineStore('counter', {
   state: () => ({ count: 0 }),
   actions: {
     increment() {
       this.count++
     },
   },
 })
 // Setup Store
 export const useCounterStore = defineStore('counter', () => {
   const count = ref(0)
   function increment() {
     count.value++
   }
   return { count, increment } // 必须使用 return 返回
 })
 // 这样写灵活性更高,在这里你可以使用 watch 以及其他有待探索的写法

创建完成之后就可以在组件/页面中使用:

 <script setup>
 import { useCounterStore } from '@/stores/counter'
 const store = useCounterStore()
 // 在 Setup Store 中,Store是使用 reactive 包裹,因此不需要加上 .value
 console.log(store.count) // 0
 </script>

解构时的一些注意点:

  • 不能直接使用 const { count } = store,这样会使得 count 失去响应式

  • 正确解构方法:

    • 使用 Pinia 提供的方法:import { storeToRefs } from 'pinia'
    • 然后即可安全解构 const { count } = storeToRefs(store)
  • 另外需要注意的是

    • 解构 State(Data) 以及 Getter(Computed) 时才需要使用 storeToRefs,Action(Methods) 可以直接解构

State

数据状态是 Store 的核心

定义

在 Option Store 中,State 是一个返回初始状态的函数

 import { defineStore } from 'pinia'
 const useStore = defineStore('counter', { // 第二个参数是对象形式
   // 为了完整类型推理,推荐使用箭头函数
   state: () => {
       return {
         count: 0,
       }
     },
   })

在 Setup Store 中,直接使用ref定义你所需要的状态

 import { defineStore } from 'pinia'
 import { ref } from 'vue' // 别忘记导入 ref  
 const useStore = defineStore('counter', () => { // 注意第二个参数是函数形式
     const count = ref(0)
     return { count }
 })

使用(setup语法)

Pinia 的便利无时无刻不能体现出来,如果你要访问 Store 中的数据,直接访问即可

 const store = useStore()
 ​
 // 1. 不需要像 vuex 那样进行繁琐的步骤,可以直接操作 store 中的数据
 console.log(store.count) // 0
 ​
 // 2. 该方法可以直接重置 state(这并不是清空 state,而是将其还原为默认值)
 store.$reset()
 ​
 // 3. 修改值
 store.count++
 // 或
 store.$patch({
   count: store.count + 1,
 })
 // 如果需要修改的 state 有很多,使用前者会损耗性能,而后者可以一次性更改多个属性
 ​
 // 4. 替换整个 state
 // 直接替换整个 state 会破坏响应性,应该使用 patch 来进行更改
 store.$patch({ count: 666 })
 // 或直接变更实例
 pinia.state.value = { count: 666 }
 ​
 // 5. 订阅 State
 store.$subscribe((mutation, state) => {
   // 每当状态发生变化时,将整个 state 持久化到本地存储。
   localStorage.setItem('count', JSON.stringify(state))
 })
 // 或
 watch(pinia.state, (state) => {
     localStorage.setItem('count', JSON.stringify(state))
   },{ deep: true }
 )

Getter

 export const useStore = defineStore('counter', {
   const count = ref(2)
   const doubleCount = computed(() => count.value * 2)
   return { count, doubleCount }
 })
 const store = useStore()
 console.log(store.doubleCount) // 4

其他地方写法看官方文档即可,这里使用的是组合式 API 写法

Action

 export const useStore = defineStore('counter', {
   const count = ref(0)
   function increase() {
       count.value++
   }
   return { count, increase }
 })
 const store = useStore()
 console.log(store.count) // 0
 store.increase()
 console.log(store.count) // 1

其他地方写法看官方文档即可,这里使用的是组合式 API 写法

你可以通过 store.$onAction() 来监听 action 和它们的结果。传递给它的回调函数会在 action 本身之前执行。after 表示在 promise 解决之后,允许你在 action 解决后执行一个一个回调函数。同样地,onError 允许你在 action 抛出错误或 reject 时执行一个回调函数。这些函数对于追踪运行时错误非常有用。 -- 官方文档

插件

一个持久化存储的简单案例

 export function useStorage(context) {
         // store: 目标 store
         // option: 定义传给 defineStore() 的 store 的可选对象
         const { store, options } = context
 ​
         // 判断是否需要设置为持久化
         if (options?.persist) {
                 let storage = localStorage
                 const storageList = [localStorage, sessionStorage, undefined]
 ​
                 // 判断存储方式是否存在
                 if (storageList.includes(options.storage)) {
                         storage = options.storage || localStorage
                 } else {
                         throw new Error(options.storage + '不存在')
                 }
 ​
                 // 若有本地存储,使用存储的值;否则使用默认值,并存储
                 if (!!storage.getItem(store.$id)) {
                         store.$state = JSON.parse(storage.getItem(store.$id))
                 } else {
                         storage.setItem(store.$id, JSON.stringify(store.$state))
                 }
 ​
                 // 监听数据变化,存储到自定义的 storage 中
                 store.$subscribe(() => {
                         storage.setItem(store.$id, JSON.stringify(store.$state))
                 })
         }
 }

在注册 Pinia 的时候注册该插件:

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import { useStorage } from './plugin/storage' // 引入插件
import App from './App.vue'

const app = createApp(App)

const pinia = createPinia()
pinia.use(useStorage) // 注册插件

app.use(pinia)

app.mount('#app')

在 Store 中加入自定义配置:

import { ref, computed } from 'vue'
import { defineStore } from 'pinia'

export const useCounterStore = defineStore(
        'counter',
        () => {
                const count = ref(0)
                const doubleCount = computed(() => count.value * 2)
                function increment() {
                        count.value++
                }
                function decrement() {
                        count.value--
                }

                return { count, doubleCount, increment, decrement }
        },
        { persist: true, storage: sessionStorage } // 传入的 options
)

推荐库:Pinia 持久化存储插件(1000 stars): prazdevs.github.io/pinia-plugi…(参与翻译全部中文文档)

Pinia 知识点大梳理,结尾带你做一个属于自己的插件~

转载自:https://juejin.cn/post/7222115117432995877
评论
请登录