用过pinia吗?有什么优点?
1. pinia是什么?
- 在
Vue3
中,可以使用传统的Vuex
来实现状态管理,也可以使用最新的pinia
来实现状态管理,我们来看看官网如何解释pinia
的:Pinia
是Vue
的存储库,它允许您跨组件/页面共享状态。- 实际上,
pinia
就是Vuex
的升级版,官网也说过,为了尊重原作者,所以取名pinia
,而没有取名Vuex
,所以大家可以直接将pinia
比作为Vue3
的Vuex
2. 为什么要使用pinia?
Vue2
和Vue3
都支持,这让我们同时使用Vue2
和Vue3
的小伙伴都能很快上手。pinia
中只有state
、getter
、action
,抛弃了Vuex
中的Mutation
,Vuex
中mutation
一直都不太受小伙伴们的待见,pinia
直接抛弃它了,这无疑减少了我们工作量。pinia
中action
支持同步和异步,Vuex
不支持- 良好的
Typescript
支持,毕竟我们Vue3
都推荐使用TS
来编写,这个时候使用pinia
就非常合适了 - 无需再创建各个模块嵌套了,
Vuex
中如果数据过多,我们通常分模块来进行管理,稍显麻烦,而pinia
中每个store
都是独立的,互相不影响。 - 体积非常小,只有
1KB
左右。 pinia
支持插件来扩展自身功能。- 支持服务端渲染
3. pinna使用
- 准备工作
我们这里搭建一个最新的Vue3 + TS + Vite
项目
npm create vite@latest my-vite-app --template vue-ts
pinia
基础使用
yarn add pinia
// main.ts
import {
createApp } from "vue";
import App from "./App.vue";
import {
createPinia } from "pinia";
const pinia = createPinia();
const app = createApp(App);
app.use(pinia);
app.mount("#app");
2.1 创建store
//sbinsrc/store/user.ts
import {
defineStore } from 'pinia'
// 第一个参数是应用程序中 store 的唯一 id
export const useUsersStore = defineStore('users', {
// 其它配置项
})
创建store
很简单,调用pinia
中的defineStore
函数即可,该函数接收两个参数:
name
:一个字符串,必传项,该store
的唯一id
。options
:一个对象,store
的配置项,比如配置store
内的数据,修改数据的方法等等。
我们可以定义任意数量的store
,因为我们其实一个store
就是一个函数,这也是pinia
的好处之一,让我们的代码扁平化了,这和Vue3
的实现思想是一样的
2.2 使用store
<!-- src/App.vue -->
<script setup lang="ts">
import {
useUsersStore } from "../src/store/user";
const store = useUsersStore();
console.log(store);
</script>
2.3 添加state
export const useUsersStore = defineStore("users", {
state: () => {
return {
name: "test",
age: 20,
sex: "男",
};
},
});
2.4 读取state
数据
<template>
<img alt="Vue logo" src="./assets/logo.png" />
<p>姓名:{
{ name }}</p>
<p>年龄:{
{ age }}</p>
<p>性别:{
{ sex }}</p>
</template>
<script setup lang="ts">
import {
ref } from "vue";
import {
useUsersStore } from "../src/store/user";
const store = useUsersStore();
const name = ref<string>(store.name);
const age = ref<number>(store.age);
const sex = ref<string>(store.sex);
</script>
上段代码中我们直接通过store.age
等方式获取到了store
存储的值,但是大家有没有发现,这样比较繁琐,我们其实可以用解构的方式来获取值,使得代码更简洁一点
import {
useUsersStore, storeToRefs } from "../src/store/user";
const store = useUsersStore();
const {
name, age, sex } = storeToRefs(store); // storeToRefs获取的值是响应式的
2.5 修改state
数据
<template>
<img alt="Vue logo" src="./assets/logo.png" />
<p>姓名:{
{ name }}</p>
<p>年龄:{
{ age }}</p>
<p>性别:{
{ sex }}</p>
<button @click="changeName">更改姓名</button>
</template>
<script setup lang="ts">
import child from './child.vue';
import {
useUsersStore, storeToRefs } from "../src/store/user";
const store = useUsersStore();
const {
name, age, sex } = storeToRefs(store);
const changeName = () => {
store.name = "张三";
console.log(store);
};
</script>
2.6 重置state
- 有时候我们修改了
state
数据,想要将它还原,这个时候该怎么做呢?就比如用户填写了一部分表单,突然想重置为最初始的状态。 - 此时,我们直接调用
store
的$reset()
方法即可,继续使用我们的例子,添加一个重置按钮
<button @click="reset">重置store</button>
// 重置store
const reset = () => {
store.$reset();
};
当我们点击重置按钮时,store
中的数据会变为初始状态,页面也会更新
2.7 批量更改state
数据
如果我们一次性需要修改很多条数据的话,有更加简便的方法,使用store
的$patch
方法,修改app.vue
代码,添加一个批量更改数据的方法
<button @click="patchStore">批量修改数据</button>
// 批量修改数据
const patchStore = () => {
store.$patch({
name: "张三",
age: 100,
sex: "女",
});
};
- 有经验的小伙伴可能发现了,我们采用这种批量更改的方式似乎代价有一点大,假如我们
state
中有些字段无需更改,但是按照上段代码的写法,我们必须要将state中的所有字段例举出了。 - 为了解决该问题,
pinia
提供的$patch
方法还可以接收一个回调函数,它的用法有点像我们的数组循环回调函数了。
store.$patch((state) => {
state.items.push({
name: 'shoes', quantity: 1 })
state.hasChanged = true
})
2.8 直接替换整个state
pinia
提供了方法让我们直接替换整个state
对象,使用store
的$state
方法
store.$state = {
counter: 666, name: '张三' }
上段代码会将我们提前声明的state
替换为新的对象,可能这种场景用得比较少
getters
属性
getters
是defineStore
参数配置项里面的另一个属性- 可以把
getter
想象成Vue
中的计算属性,它的作用就是返回一个新的结果,既然它和Vue
中的计算属性类似,那么它肯定也是会被缓存的,就和computed
一样
3.1 添加getter
export const useUsersStore = defineStore("users", {
state: () => {
return {
name: "test",
age: 10,
sex: "男",
};
},
getters: {
getAddAge: (state) => {
return state.age + 100;
},
},
})
上段代码中我们在配置项参数中添加了getter
属性,该属性对象中定义了一个getAddAge
方法,该方法会默认接收一个state
参数,也就是state
对象,然后该方法返回的是一个新的数据
3.2 使用getter
<template>
<p>新年龄:{
{ store.getAddAge }}</p>
<button @click="patchStore">批量修改数据</button>
</template>
<script setup lang="ts">
import {
useUsersStore } from "../src/store/user";
const store = useUsersStore();
// 批量修改数据
const patchStore = () => {
store.$patch({
name: "张三",
age: 100,
sex: "女",
});
};
</script>
上段代码中我们直接在标签上使用了store.gettAddAge
方法,这样可以保证响应式,其实我们state
中的name
等属性也可以以此种方式直接在标签上使用,也可以保持响应式
3.3 getter
中调用其它getter
export const useUsersStore = defineStore("users", {
state: () => {
return {
name: "test",
age: 20,
sex: "男",
};
},
getters: {
getAddAge: (state) => {
return state.age + 100;
},
getNameAndAge(): string {
return this.name + this.getAddAge; // 调用其它getter
},
},
});
3.3 getter
传参
export const useUsersStore = defineStore("users", {
state: () => {
return {
name: "test",
age: 20