个人简介
👀个人主页: 前端杂货铺
🙋♂️学习方向: 主攻前端方向,正逐渐往全干发展
📃个人状态: 研发工程师,现效力于中国工业软件事业
🚀人生格言: 积跬步至千里,积小流成江海
🥇推荐学习:🍍前端面试宝典 🍉Vue2 🍋Vue3 🍓Vue2/3项目实战 🥝Node.js🍒Three.js 🍖JS版算法
🌕个人推广:每篇文章最下方都有加入方式,旨在交流学习&资源分享,快加入进来吧
Pinia
内容 | 参考链接 |
---|---|
Vue3正式发布那么久了,你认识Pinia了吗? | 认识Pinia,搭建开发环境 |
文章目录
✨✨前言
各位新朋友、老朋友们大家好,这里是前端杂货铺,欢迎各位的到来!!
上篇文章 我们初步认识了 Pinia,并进行了项目环境的搭建。接下来,我们从 Pinia 的核心模块着手,继续学习吧~
✨✨Store
store 就是 数据仓库 ,我们可以把它看成一个 只存放数据的公共组件。
🎈🎈🎈State
state 是 store 的核心,表示当前模块的状态,里面存放着我们需要使用的数据。
我们在 store 文件夹下新建 user.ts
文件,导入 defineStore(用于定义 Store)。一般 const 定义的常量都为 useXxxStore
,其中 Xxx
是 defineStore 的第一个参数 xxx
。
之后我们在 state 中定义 age 属性。
.store/user.ts
typescript
import { defineStore } from "pinia";
/**
* pinia 中使用 definStore 方法来定义 store
* 第一个参数是应用程序中的 store 的唯一 id
* 第二个参数是一个对象,store 的配置项,比如配置 store 内的数据
*/
export const useUserStore = defineStore('user', {
// state 为 store 的核心,表示当前模块的状态
// 为了完整类型推理,推荐使用箭头函数
state: () => {
return {
age: 22
}
}
})
在 App.vue
组件中,我们导入刚才定义的 useUserStore,并定义 userStore
常量接收 useUserStore
定义模块的相关状态,之后就可以在模板中通过 userStore.xxx
的方式进行响应式获取了。
我们可以如下定义 add() 方法,使得 userStore.age += 1
,在模板中的 <button></button>
中调用 add()
方法,便可以实现每点击一次按钮加一。
App.vue
typescript
<template>
<div class="text">
Hello pinia,I am {{ userStore.age }} years old.
</div>
<button @click="add">+1</button>
</template>
<script setup lang="ts">
import { useUserStore } from './store/user'
// 使用 useUserStore 并返回定义模块的相关状态以及方法
const userStore = useUserStore()
const add = () => {
// userStore 模块里面的 state 是响应式数据
userStore.age += 1
}
</script>
<style>
.text {
background: orange;
}
</style>
🎈🎈🎈Getters
Getter
是 defineStore 参数配置项(State、Getter、Action
)里面的一个属性。Getter
属性是一个对象,该对象里面是各种方法。我们可以把 Getter 看成 Vue 中的计算属性,它的作用就是返回一个新的结果。
注:与计算属性类似,Getter
也有缓存效果。(值会基于其响应式依赖被缓存。值仅会在其响应式依赖更新时才重新计算。)
在 defineStore 的第二个参数(对象)中,我们定义 getters
,在里面我们可以定义一些计算方法:
- 可以直接写箭头函数,把 state 中的值传进来,处理之后并返回
- 可以在计算方法中调用其他计算方法,但这时要使用普通函数的写法,
注:函数要加上类型限制,不然报错
- 不同于 Vue 的计算属性,pinia 中的计算方法可以直接接收参数,注意参数写在 return 返回的函数
()
中
.store/user.ts
typescript
import { defineStore } from "pinia";
/**
* pinia 中使用 definStore 方法来定义 store
* 第一个参数是应用程序中的 store 的唯一 id
* 第二个参数是一个对象,store 的配置项,比如配置 store 内的数据
*/
export const useUserStore = defineStore('user', {
// state 为 store 的核心
state: () => {
return {
name: '前端杂货铺',
age: 20
}
},
getters: {
/**
* 箭头函数的写法,直接把 state 中的值传进来使用
* @param state
* @returns
*/
doubleAge: (state) => state.age * 2,
/**
* 如果要在 Getter 中调用其他的计算属性方法,不能使用箭头函数
* 注意:需要自己定义当前方法的返回值类型
* @returns
*/
getNameAndAge():string {
return this.name + " and " + this.doubleAge;
},
/**
* 接收页面上传过来的参数,我们用年龄加上它并返回
* @returns
*/
getAddAge() {
return (num: number) => this.age + num;
}
},
})
我们通过 useUserStore() 创建 userStore 实例,就可以通过 X.x
的方式使用了。
下面的代码中,我们不仅仅练习了计算属性的多种用法,还使用了 ref 作对比。从页面显示的结果中我们可以发现,当点击 Getter +1
按钮时,和计算属性相关的数据会发生改变(前四个会变) 。当点击 Ref +1
时,和计算属性相关的数据不会发生改变(仅最后一个会变),进一步说明了 Getter 的缓存性。
App.vue
typescript
<template>
<div style="background: orange">
##state## Hello pinia,I am {{ userStore.age }} years old.
</div>
<div style="background: skyblue">
##getters箭头函数## Hello pinia,I am {{ userStore.doubleAge }} years old.
</div>
<div style="background: yellow">
##getters普通函数##,Hello pinia,I am {{ userStore.getNameAndAge }} years
old.
</div>
<div style="background: yellowgreen">
##getters计算属性传递参数##,Hello pinia,I am
{{ userStore.getAddAge(5) }} years old.
</div>
<button @click="add">##Getter## +1</button>
<div style="background: pink">##ref响应数据##,num: {{ num }}</div>
<button @click="numAdd">##Ref## +1</button>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { useUserStore } from "./store/user";
// 使用 useUserStore 并返回定义模块的相关状态以及方法
const userStore = useUserStore();
const add = () => {
// userStore 模块里面的 state 是响应式数据
userStore.age += 1;
};
// ref 响应式数据
const num = ref(1);
const numAdd = () => {
num.value += 1;
};
</script>
🎈🎈🎈Action
前面提到的 State 和 Getter 属性都主要是 数据层面的 ,并没有具体的业务逻辑代码,它们两个就和我们组件代码中的 data 数据和 computed 计算属性一样。如果我们有 业务代码 的话,最好写在 Actions 中,该属性就和我们组件代码中的 methods 相似,用来放置一些处理业务逻辑的代码。
- state 对应 data
- getter 对应 computed
- action 对应 method
在 actions
中,我们定义修改 name 的同步方法 saveName()
和 异步方法 savaName2()
,用于在 App.vue 组件中使用。
./store/user.ts
typescript
import { defineStore } from "pinia";
// 异步方法,延迟 1s
const dely = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(100)
}, 1000)
})
}
/**
* pinia 中使用 definStore 方法来定义 store
* 第一个参数是应用程序中的 store 的唯一 id
* 第二个参数是一个对象,store 的配置项,比如配置 store 内的数据
*/
export const useUserStore = defineStore('user', {
// state 为 store 的核心
state: () => {
return {
name: '前端杂货铺',
age: 20
}
},
/**
* 当前模块的相关方法
* 存放当前模块的相关业务逻辑函数
*/
actions: {
/**
* 在 pinia 中,可以直接通过 actions 修改 state
* pinia 的 actions 里面既可以是同步也可以是异步
* @param name
*/
saveName(name: string) {
this.name = name;
},
/**
* 调用异步方法,延迟 1s 更新 name 的值
* @param name
*/
async saveName2(name: string) {
// 延迟 1s 更新
await dely();
this.name = name;
},
}
})
App.vue
updateName 调用 saveName() 同步方法,点击名字立即由 前端杂货铺 => 3号杂货铺 。
updateName2 调用 saveName2() 异步方法,点击名字延迟 1s 后由 前端杂货铺 => 5号杂货铺。
typescript
<template>
<div style="background: orange">
Hello pinia,My name is {{ userStore.name }} .
</div>
<button @click="updateName">同步修改</button>
<button @click="updateName2">异步修改</button>
</template>
<script setup lang="ts">
import { useUserStore } from "./store/user";
// 使用 useUserStore 并返回定义模块的相关状态以及方法
const userStore = useUserStore();
const updateName = () => {
// 直接使用 userStore 调用 actions 里定义的方法
userStore.saveName('3号杂货铺')
}
const updateName2 = () => {
userStore.saveName2('5号杂货铺')
}
</script>
🎉🎉本篇小结
本篇文章介绍了 Pinia 的核心概念。
store
是我们的数据仓库,在这里存放的数据可以在任意组件中使用,我们只需要在组件中 import
导入进来再实例化就可以使用了。
state
是 store
的核心,在此存放我们需要的数据。state 类似于组件中的 data。
getter
等同于 store
的 state
的计算值,大多数情况下,getter
仅依赖于 state
,不过它们也可能会使用其他 getter
,当我们使用常规的函数定义 getter
时,可以通过 this
访问到整个 store
实例。getter
类似于组件中的 computed
。
不同于 state
和 getter
针对的数据层面,action
针对的是业务逻辑层面,在 actions
中我们可以定义同步的方法,也可以定义异步的方法。actions
相当于组件中的 methods
。
好啦,本篇文章到这里就要和大家说再见啦,祝你这篇文章阅读愉快,你下篇文章的阅读愉快留着我下篇文章再祝!
参考资料:
- Pinia 官方文档
- Pinia 教程 【作者:千锋教育】