您现在的位置是:首页 >其他 >Vue--》探索Pinia:Vue状态管理的未来网站首页其他

Vue--》探索Pinia:Vue状态管理的未来

亦世凡华、 2024-06-17 10:32:14
简介Vue--》探索Pinia:Vue状态管理的未来

目录

Pinia的讲解与使用

Pinia的安装与使用

store数据操作

解构store数据

actions-getters的使用

Pinia常用API

持久化插件


Pinia的讲解与使用

Pinia 是由 Eduardo San Martin Morote 创建的,这是一个轻量级的、使用 Vue3 Composition API 的状态管理库。Pinia 于2020年8月正式发布,也就是在 Vue 3.0.0 版本推出后不久,可以说是比较新的一个状态管理解决方案。虽然 Pinia 是一种相对较新的解决方案,但它受到了许多 Vue.js 开发者的青睐,并在许多项目中得到广泛应用。

官方文档为:https://pinia.vuejs.org/zh/introduction.html ,从官网的图示我们不难看出,Pinia是专为 Vue.js 框架设计的状态管理解决方案,其核心是建立在 Vue 3 Composition API 之上的。因此Pinia 不能直接用于使用其他框架的项目。

但是,Pinia 可以与其他框架和库一起使用,例如在通过 Vue.js 构建的应用程序中,如果需要在 React 组件中使用 Pinia 中的状态,可以使用 Vue.js 手动渲染 React 组件的方法来实现。但是这种使用方式需要额外处理,使用起来可能会有一定的挑战性。如果你正在使用React,可以考虑使用专为React设计的状态管理方案,比如Redux,Mobx, Recoil等等。

那么我们在项目中该如何选择合适的状态管理工具进行使用?也就是说Pinia和Vuex的区别和应用场景到底有什么区别,别急,等我慢慢道来。

Pinia 和 Vuex 是 Vue.js 框架中两种常见的状态管理解决方案。它们都是用于管理响应式状态,但存在一些区别。如下:

API 的不同

Pinia 提供了一个更简单、更直接、易于理解的 API;而 Vuex 的 API 更加灵活,但也更为复杂,需要花费更多的时间学习。

全局状态的处理不同

在 Vuex 中有一个全局的 store(仓库),用于存储应用程序中的所有状态,而 Pinia 中则是一个基于类的 API,每个组件实例均有自己的 Pinia store,这使得应用程序的状态管理更加灵活、容易。此外,Pinia 还具有更好的 TypeScript 支持。

API 实时性的差异

在 Pinia 中,所有 getter 和 action 都会被实时更新,而在 Vuex 中只有 getter 会被实时更新,action 不会。

那么Pinia到底有啥特点能够吸引Vue开发者的对其竞相追逐呢?如下:

1)Pinia 是使用 TypeScript 编写的,它充分利用了 TypeScript 强类型系统的优点,提供了更好的类型声明和类型检查能力。

2)Pinia 的代码结构更加简洁明了,由于 Vue.js 3 在状态管理方面提供了更好的支持,Pinia 可以借助 Vue.js 3 的一些新特性来实现更简单、更直观的状态管理方案。

3)Pinia 支持插件,它提供了一个插件 API,可以将它与其他库和工具集成使用,如 devtools、vuex-persistedstate 等。

4)Pinia 通过提供 API 来支持响应式和异步操作,是一个更加灵活和可配置的状态管理方案。

5)在使用 Pinia 时,不需要像 Vuex 一样集中式地管理所有的状态,而是可以分成多个 Store,每个 Store 可以管理自己相关的状态。这种分离使代码结构更加清晰,易于维护。

总之,Pinia 相较于 Vuex 更加简单方便,不需要使用复杂的 API 和语法来管理应用状态,它还能够适应更广泛的应用场景,而 Vuex 则更加适用于复杂的状态管理需求。

Pinia的安装与使用

这里我采用的是vite构建工具进行创建的 vue3 最新项目,并结合TS的语言支持,如果你想在vue2中使用pinia,可自行查阅官网,这里不再赘述。如果不了解vite构建工具的朋友推荐看一下我之前讲解的文章:vite脚手架的搭建与使用 ,当然如果你是现成已经创建好的项目,直接执行如下命令安装库即可,这里不再赘述。

创建完项目后,终端执行如下命令进行安装Pinia库:

npm install pinia

安装完成之后,我们就可以在main.ts中进行引入Pinia相关配置代码,创建一个 pinia 实例 (根 store) 并将其传递给应用,如下:

import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia'

const store = createPinia()
let app = createApp(store)
app.use(store)

createApp(App).mount('#app')

初始化仓库store: Store (如 Pinia) 是一个保存状态和业务逻辑的实体,它并不与你的组件树绑定。换句话说,它承载着全局状态。它有点像一个永远存在的组件,每个组件都可以读取和写入它。一般我们通常在 src 目录下新建文件夹 store ,里面存放中我们初始化仓库的文件代码:

在Pinia中 store是用defineStore()定义的,它的第一个参数是一个独一无二的名字,作为store中的唯一ID,Pinia 将用它来连接 store 和 devtools。为了方便处理ID,我将其单独抽离出来一个文件,用来枚举所有的pinia仓库ID,如下:

export const enum Names {
  Student = 'Student'
}

在store文件夹下新建index.ts文件,用来存放数据和操作的数据的方法:

import { defineStore } from "pinia";
import { Names } from "./storeName";

// 将返回的函数命名为 use... 是一个符合组合式函数风格的约定。
export const useStudentStore = defineStore(Names.Student,{
  state:()=>({
    name:'张三',
    age:18
  })
})

在App.vue中我们进行引入store并将store中的数据进行渲染到界面上:

<template>
  <div class="">
    Pinia:{{ Student.age }}
  </div>
</template>

<script setup lang="ts">
import { useStudentStore } from "./store"
const Student = useStudentStore()
</script>

在 vue 开发者工具中我们也可以看到我们创建的pinia数据。

store数据操作

直接修改

在pinia中我们可以直接对store中的数据进行操作,不再像vuex一样需要借助action才能进行:

<template>
  <div class="">
    Pinia:{{ Student.age }}
    <button @click="change">修改</button>
  </div>
</template>

<script setup lang="ts">
import { useStudentStore } from "./store"
const Student = useStudentStore()
const change = () =>{ 
  Student.age++
}
</script>

$patch函数修改

如果想批量修改数据的话可以借助store身上的一个 $patch 函数,传入一个对象进行批量修改:

<template>
  <div class="">
    Pinia:{{ Student.name }}
    Pinia:{{ Student.age }}
    <button @click="change">修改</button>
  </div>
</template>

<script setup lang="ts">
import { useStudentStore } from "./store"
const Student = useStudentStore()
const change = () =>{ 
  Student.$patch({
    name:'李四',
    age:20
  })
}
</script>

当然也可以采用函数式写法:

$state函数修改

当然也可以采用$state函数进行修改,但是缺陷是必须将state对象中的所有数据都进行修改,不能只单独修改某一个。

action函数修改

这里也可以借助store代码中的actions方法进行操作state中的数据,如下:

解构store数据

当我们使用store时,也可以采用解构方式,但是解构出来的store数据是非响应式的,如下:

<template>
  <div class="">
    Pinia:{{ Student.name }}
    Pinia:{{ Student.age }}
    <p>解构后的数据</p>
    Pinia:{{ name }}
    Pinia:{{ age }}
    <button @click="change">修改</button>
  </div>
</template>

<script setup lang="ts">
import { useStudentStore } from "./store"
const Student = useStudentStore()
const { name,age } = Student
const change = () =>{ 
  Student.setChange('小张',10)
}
</script>

如果想将解构后的数据设置为响应式,可以采取这种方式

actions-getters的使用

在pinia中actions属性中可以调用同步函数和异步函数,如下:

同步函数的简单使用

import { defineStore } from "pinia";
import { Names } from "./storeName";

type User = {
  name:string,
  age:number
}
let result:User = {
  name:'小王',
  age:100
}

// 将返回的函数命名为 use... 是一个符合组合式函数风格的约定。
export const useStudentStore = defineStore(Names.Student,{
  state:()=>({
    person:<User>{}
  }),
  actions:{
    setChange(){
      this.person = result
    }
  }
})

在App.vue中直接调用函数即可:

异步函数的简单使用

import { defineStore } from "pinia";
import { Names } from "./storeName";

type User = {
  name:string,
  age:number
}
const Login = ():Promise<User>=>{
  return new Promise((resolve)=>{
    setTimeout(()=>{
      resolve({
        name:'小明',
        age:100
      })
    },2000)
  })
}
// 将返回的函数命名为 use... 是一个符合组合式函数风格的约定。
export const useStudentStore = defineStore(Names.Student,{
  state:()=>({
    person:<User>{},
    name:'老王'
  }),
  actions:{
    async setChange(){
      const result = await Login( )
      this.person = result
      this.setName('小王')
    },
    setName(name:string){
      this.name = name
    }
  }
})

在pinia中getters属性中可以修饰一些值,如下:

Pinia常用API

pinia给我们也提供了一些常用的API,便捷我们日常的开发,如下:

$reset:重置state状态数据

$subscribe:响应state的变化

$onAction:响应actions的变化

持久化插件

pinia和vuex都有一个通病,就是页面一旦刷新数据就会丢失,我们希望刷新页面之后,数据将会在页面中进行保留,详情代码都放置在 mian.ts 中,如下:

import { createApp,toRaw } from 'vue'
import App from './App.vue'
import { createPinia, PiniaPluginContext } from 'pinia'

const setStorage = (key:string,value:any) => {
  localStorage.setItem(key,JSON.stringify(value))
}
const getStorage = (key:string) =>{ 
  return localStorage.getItem(key) ? JSON.parse(localStorage.getItem(key) as string) : {}
}

type options = {
  key?:string
}
const __piniakey__:string = 'null'
const piniaPlugin = (options:options) => {
  return (context:PiniaPluginContext) => {
    const { store } = context
    const data = getStorage(`${options?.key ?? __piniakey__}-${store.$id}`)
    store.$subscribe(()=>{
      setStorage(`${options?.key ?? __piniakey__}-${store.$id}`,toRaw(store.$state))
    })
    console.log(store,'store')
    return {
      ...data
    }
  }
}
const store = createPinia()
store.use(piniaPlugin({
  key:'pinpa'
}))
let app = createApp(store)
app.use(store)

createApp(App).mount('#app')

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。