Pinia 的学习路程

1,201 阅读4分钟

1. 前言

Pinia 在未来中有可能成为vue3中的新一代的状态管理,官方文档


2. Pinia 的介绍

2.1 我们为什么要了解 Pinia?

在我们开发技术栈为 Vue 的项目中,我们会经常用到 vuex, 在使用 vuex 的时候,我们有没有遇到过这样的问题:

  • 改变某一个 state 的值,如果是同步更新需要 Mutations,否则要用 Actions
  • 给 vuex 中的 state 添加 TypeSCript, 不是特别的容易
  • 把 vuex 中的 state 分成多个部分, 就要用到 module

如果你想避免上面的问题,你可以试着学习一下 Pinia

2.2 什么是 Pinia?

Pinia在2019年11月开始时候是一个实验项目,目的就是重新设计一个与组合API匹配的vue状态存储。基本原则和原来还是一样的,pinia同时支持vue2和vue3,比不要求你必须使用组合API。不管是使用vue2或者vue3,pinia的API是相同的,文档是基于vue3写的,同时在需要的地方也标注了vue2的用法,所以不管你是使用vue2还是vue3开发,都能够通过这个文档来学习。

Pinia 的作者是vue核心团队成员,目前 Pinia 已纳入官方中,正式使用了

Pinia.png

  • Vue2 和 Vue3都支持

    • 除来初始化安装和 SSR配置之外,两者的 API 都是相同的
    • 官方文档中主要针对 vue3 进行说明,必要的时候会提供 vue2 的注释
  • 支持 Vue DevTools

    • 跟踪 actions、mutiaons 的时间线
    • 在使用容器的组件中就可以观察到容器本身
    • 支持 time travel 更容易的调试功能
    • 在 vue2 中的 Pinia 使用 vuex 现有的接口,所有不能与 vuex 一起使用
    • 但是针对 vue3 中的调试工具支持还不够完美,比如还没有 time-travel 的调试功能
  • 模块热更新

    • 无需重新加载页面即可修改您的容器
    • 热更新的时候保持任何现有的状态
  • 支持使用插件扩展 Pinia 功能

  • 相比 vuex 有更好的完美的 TypeScript 支持

  • 支持服务端渲染

2.3 核心概念

Pinia 从使用的角度和之前的 vuex 几乎是一样的

Store(如Pinia) 是一个保存状态和业务逻辑的实体,它不绑定到您的组件树。换句话,它承载全局 state 它有点像一个始终存在的组件,每个人都可以读取和写入,它有三个核心概念

  • state:类似组件中的 data,用来存储全局状态
  • getter: 类似组件中的comnuted,根据已有的 state 封装派生数据,也具有缓存的特性
  • actions: 类似组件中的methods 用来封装业务逻辑, 支持同步和异步

3. 快速入门

3.1 安装

yarn add pinia
or
npm install pinia

3.2 初始化配置

此处以 Vue3 为例子:

import { createApp } from 'vue'
import { createPinia } from 'pinia'

const pinia = createPinia()
const app = createApp(App)
app.use(pinia).mount('#app')

如果您使用的是 Vue2,您还需安装一个插件并将 created 注入 pinia到应用程序的根目录:

import { createPinia, PiniaVuePlugin } from 'pinia'

Vue.use(PiniaVuePlugin)
const pinia = createPinia()

new Vue({
  el: '#app',
  ...
  pinia
})

3.3 目录结构

src
├── store                        
│  ├── index.js 		 # 模块名称
│  ├── ....
│  └── user.js

基础示例和总结:

store/index.js

import { defineStore } from 'pinia'

/**
* 定义并导出容器
* defineStore:定义api
* 参数1: 容器的ID, 必须唯一,例如 apps
* 参数2: 选项对象,例如:state、getters、actions
* 返回值: 一个函数,调用得到容器实例
*/
export cosnt useAppsStore = defineStore('apps', {
  /**
  * 注意事项:
  * 1.必须是函数,这样是为了在服务端渲染的时候避免交叉请求导致的数据状态的污染
  * 2.必须是箭头函数,这是为了更好的 TS 类型推导
  */
  state: () => {
    return {
      count: 100,
      name: 'pinia',
      arr: [1,2,3]
    }
  },
  
  getters: {
    /**
    * 函数接受一个可选的参数:state 状态对象
    * 也可以使用:this, 例如: this.count + 10
    */
    count10 (state) {
      console.log('--- count10 调用了---')
      return state.count + 10
    }
  },
  
  /**
  * 注意事项:
  * 不能使用箭头函数定义 actions, 因为箭头函数绑定外部 this
  */
  actions: {
    changeState(num) {
      /**
      * 第一种调用方式:
      * 修改多个数据时,没有做性能的优化
      */
      this.count += num
      this.name = 'pinia4',
      this.arr.push(5)
      
      /**
      * 第二种调用方式: 如果需要修改多个数据,建议使用 $patch 批量更新
      * 修改多个数据时,$patch 批量修改做了一个性能的优化
      */
      // this.$patch({ ... })
      
      /**
      * 第三种调用方式: $patch, 一个函数, 批量更新
      * state: 容器中的 state
      */
      // this.$patch(state => { ... })
    }
  }
})

App.vue

<template>
 <h1>方式1:{{ appStore.count }}</h1>
 <hr/>
 <h1>直接结构拿到的数据:{{ count }}</h1>
 <hr/>
 <h1>使用storeToRefs拿到的数据:{{ count }}</h1>
 <button @click="handleChangeState">修改数据</button>
 <hr/>
 <h1>调用getters:{{ appStore.count10 }}</h1>
</template>

<script setup>
  import { useAppsStore } from '@/store'
  import { storeToRefs } from 'pinia'
  
  const appStore = useAppsStore()
  
  // 这样是有问题的,因为这样拿到的数据不是响应式的数据,是一次性的
  const { count, name } = appStore
  
  // 解决的办法就是使用 pinia 中的API:storeToRefs
  // 结构出来的数据做了 ref 响应式代理,在访问的时候要以.value的形式, 如: count.value
  const { count, name } = storeToRefs(appStore)
  console.log(count.value) // 10
  
  const handleChangeState = () => {
    /**
    * 第一种调用方式:
    * 修改多个数据时,没有做性能的优化
    * 修改一次,更新一次视图
    */
    appStore.count++
    
    /**
    * 第二种调用方式: 如果需要修改多个数据,建议使用 $patch 批量更新
    * 修改多个数据时,$patch 批量修改做了一个性能的优化
    * 等所有数据修改完成后,再跟新视图
    * 不易做复杂的处理
    */
    appStore.$patch({
      count: appStore.count + 1,
      name: 'pinia1'
    })
    
    /**
    * 第三种调用方式: $patch, 一个函数, 批量更新
    * state: appStore 容器中的 state
    * 方便处理复杂的处理
    */
    appStore.$patch(state => {
      state.count++
      state.name = 'pinia2',
      state.arr.push(4)
    })
    
    /**
    * 第四种调用方式: 逻辑比较多的时候可以封装到 actions 做处理
    */
    appStore.changeState(10)
    
  }
  
</script>

以上是Pinia的基本的介绍和基本的使用方式和在使用中注意的点;

Pinia 官方文档

最后:第一次写文章,如果伙伴觉得写的不好,希望大家多见谅!如果觉得还可以,对你有帮助的话,可以点个赞,支持一下哦!