新一代的状态管理器?关于 Pinia 的全方位解析!
前言
大家好,我是 CoderBin,本文将给大家分享Pinia的相关知识和使用技巧。希望对大家有所帮助,谢谢。
创作不易,你们的点赞收藏留言就是我最大的动力💓
如果文中有不对、疑惑的地方,欢迎各位小伙伴们在评论区留言指正🌻
为什么要使用 Pinia:
Pinia.js 是新一代的状态管理器,由 Vue.js团队中成员所开发的,因此也被认为是下一代的 Vuex,即 Vuex5.x,在 Vue3.0 的项目中使用也是备受推崇。
Pinia 是 Vue 的存储库,它允许您跨组件/页面共享状态。 如果您熟悉 Composition API,您可能会认为您已经可以通过一个简单的
export const state = reactive({})
. 这对于单页应用程序来说是正确的,但如果它是服务器端呈现的,会使您的应用程序暴露于安全漏洞。 但即使在小型单页应用程序中,您也可以从使用 Pinia 中获得很多好处:
- dev-tools 支持
- 跟踪动作、突变的时间线
- Store 出现在使用它们的组件中
- time travel 和 更容易的调试
- 热模块更换
- 在不重新加载页面的情况下修改您的 Store
- 在开发时保持任何现有状态
- 插件:使用插件扩展 Pinia 功能
- 为 JS 用户提供适当的 TypeScript 支持或 autocompletion
- 服务器端渲染支持
1. 学习初始
这里使用 vite 来快速创建一个vue3项目,这里选择 TS 编写(使用 JS 或 TS 都可以)
npm init vite@latest
2. 安装
npm i pinia
3. 基本配置
3.1 在 main.ts 文件中,有以下三个步骤:
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
// 1. 导入 pinia
import { createPinia } from 'pinia'
// 2. 创建 pinia 实例
const pinia = createPinia()
// 3. 挂载到 Vue 根实例
app.use(pinia)
app.mount('#app')
3.2 创建仓库
在 src 目录下创建 /store/index.ts
pinia 仓库
import { defineStore } from "pinia";
export const useMineStore = defineStore('main', {
state: () => {
return {}
},
actions: {
},
getters: {
}
})
这样,一个 pinia 仓库就构建完成啦!
4. 仓库解读
- 定义并导出容器
- 参数 1:容器的 ID,必须唯一,将来 Pinia 会把所有的容器挂载到根容器(命名合法即可)
- 参数 2:选项对象
- 返回值:一个函数,调用得到容器实例
每个选项的解释如下:
import { defineStore } from "pinia";
export const useMineStore = defineStore('main', {
//* 类似于组件中的 data,用来存储全局状态
// 1. 必须是函数:这样是为了在服务器渲染的时候避免交叉请求导致的数据转态污染
// 2. 必须是箭头函数:这是为了更好的 TS 类型推导
state: () => {
return {
count: 100,
foo: 'bar'
}
},
//* 类似于组件的 methods,封装业务逻辑,修改 state
// 不要用 箭头函数定义 actions ,否则里面无法使用 this!因为箭头函数绑定的是外部 this
actions: {
},
//* 类似组件的 computed,封装计算属性,具有缓存功能
getters: {
}
})
5. 组件中使用仓库数据
步骤: 导入仓库 -> 创建store实例 -> 获取数据
这里以 src/components
下的组件为例
<template>
<div>
<p>{{ mainStore.count }}</p>
</div>
</template>
<script lang="ts" setup>
// 1. 按需导入仓库
import { useMineStore } from '../store'
// 2. 创建 store 实例
const mainStore = useMineStore()
// 3. 使用 mianStore 中的 state 里面的数据
console.log(mainStore.count);
</script>
6. 注意解构赋值的正确方式!
如果直接使用普通的解构赋值方式,被解构出来的数据将不具备响应式!
以下通过点击按钮改变 count 的值来说明这个问题
<template>
<div>
<!-- 页面刷新时会显示初始数据,但是点击按钮修改了store里面count的数据的时候,是不会同步到这里的 -->
<p>{{ count }}</p>
<button @click="handlerChangeState">修改count</button>
</div>
</template>
<script lang="ts" setup>
import { storeToRefs } from 'pinia';
import { useMineStore } from '../store'
const mainStore = useMineStore()
// 普通的解构赋值
const { count } = mainStore
// 点击修改 count 的值,修改 store 里面的值的方式后面会讲,这里不用注意
const handlerChangeState = () => {
mainStore.count++
}
</script>
说明:通过普通解构赋值拿出来的数据,是有问题的!这样的数据不再是响应式!是一次性的!
因为 Pinia 其实是把 state
里的数据都做了 reactive 处理了(reactive直接使用普通的解构赋值的数据也不具备响应式)
解决方法:使用 Pinia 官方提供的 API,storeToRefs
<template>
<div>
<!-- 这里的 count 是响应式的!-->
<p>{{ count }}</p>
<button @click="handlerChangeState">修改count</button>
</div>
</template>
<script lang="ts" setup>
// 导入官方提供的 API
import { storeToRefs } from 'pinia';
import { useMineStore } from '../store'
const mainStore = useMineStore()
// 普通的解构赋值
// const { count } = mainStore
// 解决方法:使用 Pinia 官方提供的 API
const {count,foo } = storeToRefs(mainStore)
// 点击修改 count 的值
const handlerChangeState = () => {
mainStore.count++
}
</script>
说明:Pinia 把解构出来的数据做 ref 响应式处理了
7. 组件内修改 sotre 的数据
依旧使用上面的案例进行说明:
- 方式一:组件中直接修改 state 中的数据
<script lang="ts" setup>
import { useMineStore } from '../store'
const mainStore = useMineStore()
// 点击修改 count 的值
const handlerChangeState = () => {
// 方式一:直接修改 state 中的数据
mainStore.count++
mainStore.foo = 'hello'
}
</script>
- 方式二:
$patch({ })
接收一个修改对象,如果需要修改多个数据,建议使用 $patch 批量更新,具有性能优化的效果
<script lang="ts" setup>
import { useMineStore } from '../store'
const mainStore = useMineStore()
// 点击修改 count 的值
const handlerChangeState = () => {
// 方式二:使用 $patch({}) 修改数据
mainStore.$patch({
count: mainStore.count+1,
foo: 'hello'
})
}
</script>
- 方式三:
$patch((state)=>{ })
接收一个函数 更好的批量更新的方式:$patch 一个函数,state 则为 store 中的 state,也具有性能优化的效果
<script lang="ts" setup>
import { useMineStore } from '../store'
const mainStore = useMineStore()
// 点击修改 count 的值
const handlerChangeState = () => {
// 方式三:使用 $patch((state)=>{ }) 修改数据
mainStore.$patch(state => {
state.count++;
state.foo = 'hello';
state.arr.push(4)
})
}
</script>
- 方式四:逻辑比较多的时候可以封装到 actions 做处理
<script lang="ts" setup>
import { useMineStore } from '../store'
const mainStore = useMineStore()
// 点击修改 count 的值
const handlerChangeState = () => {
// 方式四:逻辑比较多的时候可以封装到 actions 做处理
mainStore.changeState(10)
}
</script>
回到 store/index.ts
中,在 actions 下编写对应逻辑
import { defineStore } from "pinia";
export const useMineStore = defineStore('main', {
state: () => {
return {
count: 100,
foo: '',
arr: [1, 2, 3]
}
},
actions: {
// 可以通过 this 访问到 state 中的数据
changeState(num: number) {
console.log(this);
console.log(this.count, num);
this.count += num;
this.foo = 'hello';
this.arr.push(4);
}
},
getters: {
}
})
总结:建议使用第四种方式,方便后期维护
8. getters 的使用
getter
类似计算属性,它并不会修改 state
的数据,只会通过计算得出结果用作于展示,必须要有返回值 return
8.1 函数接收一个可选参数:state 状态对象,好处是会自动判断这个函数的返回值是什么类型
import { defineStore } from "pinia";
export const useMineStore = defineStore('main', {
state: () => {
return {
count: 100,
}
},
actions: {
},
getters: {
count10(state) {
console.log('count 调用了');
return state.count + 10;
// 注意:如果接收了一个 state 参数,内部却用了 this,也是可以的
// return this.count + 10;
},
}
})
8.2 注意:如果在 getters 中的函数不接受参数 state,而内部却使用了 this,则必须手动指定返回值类型,否则 TS 类型推导不出来
import { defineStore } from "pinia";
export const useMineStore = defineStore('main', {
state: () => {
return {
count: 100,
}
},
actions: {
},
getters: {
// 手动指定返回值类型
count10(): number {
console.log('count 调用了');
return this.count + 10;
}
}
})
8.3 getters 中的函数允许相互调用
- 这里的 newArr 函数过滤了 arr 数组中为 3 的值并返回一个数组,然后被 newArr1 使用了新的数组,在组件中也可以访问 newArr1 去获取到值。
- 这只是用来演示 getters 中的函数可以相互调用,再以后的项目中,可以实现其他更好的功能。
import { defineStore } from "pinia";
export const useMineStore = defineStore('main', {
state: () => {
return {
arr: [1, 2, 3],
}
},
actions: {
},
getters: {
newArr(state) {
return state.arr.filter(item => item!==3)
},
// getters 里面的函数可以互相调用到,直接使用 this 即可
newArr2() {
return this.newArr
}
}
})
每文一句:好好学习,天天向上。
ok,关于 pinia 的简单使用就到这里,看到这里相信你对 Pinia 已经有了简单的了解。从各方面来看,Pinia 确实要比 Vuex 更容易让人接受。如果想对 Pinia 进行更加深入的理解,请移步 Pinia中文文档 深入研究。如果有什么疑问也可以在评论区留言,大家一起探讨,谢谢!
- 点赞
- 收藏
- 关注作者
评论(0)