Vue--》探索Pinia:Vue状态管理的未来
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
目录
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设计的状态管理方案比如ReduxMobx, 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开发者的对其竞相追逐呢如下
1Pinia 是使用 TypeScript 编写的它充分利用了 TypeScript 强类型系统的优点提供了更好的类型声明和类型检查能力。
2Pinia 的代码结构更加简洁明了由于 Vue.js 3 在状态管理方面提供了更好的支持Pinia 可以借助 Vue.js 3 的一些新特性来实现更简单、更直观的状态管理方案。
3Pinia 支持插件它提供了一个插件 API可以将它与其他库和工具集成使用如 devtools、vuex-persistedstate 等。
4Pinia 通过提供 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中的唯一IDPinia 将用它来连接 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')