2k字轻松入门Pinia，猴子都可以看懂的教程

前言

都什么年代了，还在用传统状态管理库？快来学习Pinia吧！

Pinia 这个名字的来源也很有意思，在西班牙语中，Pinia 是菠萝这个词最相似的英语发音，而一个菠萝是由一组组单独的花，结合在一起的，创造了多个水果，与 stores 类似，每一个都是独立的，但最终都会有着联系

当我们点开vuex的 github 库的时候，会看见官方的提示Pinia is now the new default，而作为 Vue 的新一代的官方状态管理库，Pinia 有着更多优势，解决了很多 Vuex 所留下的问题，在编写的时候，会更有逻辑性，接下来就让我们去试着了解一下吧！

Pinia 优点

vue3 vue2 均支持

抛弃了 Mutation 的操作，只有state、getter和action

Actions支持同步和异步

支持使用插件扩展 Pinia 功能

不需要嵌套模块，更加符合 Vue3 的Composition api

支持 typescript

代码更加的简洁

用 Pinia 方式创建一个 store

我们先快速创建一个空项目，再安装Pinia:

Copy
npm install pinia

虽然 Pinia 支持 vue2，但如果你使用的 vue 版本低于Vue2.7, 还需独立安装 composition api: @vue/composition-api（这里建议直接升到 Vue2.7，相较于 Vue3 来说，跨度不会太大，但对 Vue 的生态支持的更好）

在main.ts中对 Pinia 进行引入：

Copy
import { createApp } from 'vue'
import { createPinia } from 'pinia'

import App from './App.vue'

const app = createApp(App)

app.use(createPinia())

app.mount('#app')

接着，我们在src/store中创建counter.ts，并写下基础模板：

Copy
import { defineStore } from "pinia";

export const mainStore = defineStore('main', {
state: () => {
return {
helloWord: 'HelloWorld'
}
},
getters: {

},
actions: {

}
})

创建好仓库mainStore(仓库名)后，我们将在组件中进行使用

Copy

{{ store.helloWord }}



import { mainStore } from "../store/counter";
const store = mainStore();




当页面显示出helloWorld后，则说明store创建成功

Pinia 改变数据状态

我们在counter.ts中的state添加数据count：

Copy
import { defineStore } from "pinia";

export const mainStore = defineStore('main', {
state: () => {
return {
count: 0,
helloWord: 'HelloWorld'
}
},
getters: {

},
actions: {

}
})

做一个拥有点击事件按钮：

Copy


修改数据状态




import { mainStore } from "@/stores/counter";
const store = mainStore();
const handleClick = () => {
store.count++;
};


将其引入App.vue后，对数据状态进行修改：

Copy

<Click />
<CountButton />



import Click from "./components/Click.vue";
import CountButton from "./components/CountButton.vue";


这时候便发现点击按钮后会改变count的值了

在实际开发的过程中，我们往往需要多次调用store中的数据，如果每次改变其值都需要{{store.****}}未免太麻烦了，我们对其进行解构：

Copy

{{ store.helloWord }}
{{ store.count }}


{{ helloWord }}
{{ count }}



import { mainStore } from "../store/counter";
import { storeToRefs } from "pinia";
const store = mainStore();

// 进行解构
const { helloWord, count } = storeToRefs(store);




注意，解构必须要用到storeToRefs()函数！

PInia 修改数据的四种方法

第一种方法：

Copy
const handleClick = () => {
store.count++;
};

第二种方式$patch

Copy
const handleClickPatch=()=>{
store.$patch({
count:store.count+2
})
}

第二种方法写起来虽然没有第一种简单，但更加适合于多个数据的改变

第三种方式 $patch 传递函数

Copy
const handleClickMethod = () => {
// 这里的state就是指代的仓库中的state
store.$patch((state) => {
state.count++;
state.helloWord = state.helloWord === "jspang" ? "Hello World" : "jspang";
});
};

第四种方式 action

当业务逻辑很复杂的时候，就将方法写在store中的action里

Copy
actions: {
changeState() {
this.count++
this.helloWord = 'jspang'
}
}

Getters

与 vuex 中的 getters 是差不多的，相当于 vue 中的计算方法。但当我们查看 vuex 文档，会发现这个提示：

注意

从 Vue 3.0 开始，getter 的结果不再像计算属性一样会被缓存起来。这是一个已知的问题，将会在 3.1 版本中修复。详情请看 PR #1878。

直到现在，这个提示仍然存在，这也是我更推荐使用Pinia的原因之一。

Pinia 中的Getters本身内部是可以进行缓存的，用代码来验证：

Copy
getters: {
phoneHidden(state) {
// 正则表达式
console.log('getters被调用');
return state.phone.toString().replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2')
}
},

我们在组件中两次调用 phone (数据)，点开控制台进行查看

是只会出现一次的，证实了其具有缓存功能，这对于性能优化是有好处的

Pinia 中的 stores 互相调用

在实际开发中，往往不会只用到一个仓库的，仓库和仓库之间通常会有一个调度，这里我们再创建一个仓库：

Copy
import { defineStore } from "pinia";

export const nameStore = defineStore('name', {
state: () => {
return {
list: ['小红', '小美', '胖丫']
}
}
})

接着我们在counter.ts中导入，import {jspangStore} from './jspang'，在action中进行调用：

Copy
actions: {
getList() {
console.log(nameStore().list);
}
}

查看控制台，发现已经成功调用了另一个仓库的state，成功做到了store之间的相互调用

支持 VueDevtools

Pinia 虽然作为一个新人，但已经全面支持 VueDevtools 了，这对于我们在实际项目开发中的调试有着很大的帮助，值得一提的是，在打开 VueDevtools 时，我们可以看见一个很俏皮的菠萝的 logo

在开发时候的心情都更好了哈哈哈

注意：如果你使用的是 Pinia v2，请升级你的Vue Devtools到 v6 版本

Pinia 实战：修改头像

说了这么多，不如来实战一下，在实际的项目中运用一下pinia。开发背景：在我们制作网页的时候，往往会涉及到注册功能，用户头像在登录前和登录后是不一样的。如果只写一个点击事件来修改头像的话，一旦刷新或是进行了页面的跳转便会恢复原样，状态无法得到保存，这时候我们就可以请出我们的 pinia 了

注意，为了 pinia 中的数据持久化存储（存到 localstorage 或 sessionstorage 中），我们还需要安装一个插件：pinia-plugin-persist，他能够让我们的操作更加方便，这里便不展开赘述了，详细可以参考官方文档

1. 创建仓库

第一步还是先创建仓库(这里默认你已经配置好了相关环境)，创建store/user.ts，具体代码如下：

Copy
import { defineStore } from 'pinia';

export const mainStore = defineStore('main', {
state: () => {
return {
login: require('../assets/images/login.png')
}
},
// 开启持久化
persist: {
enabled: true,
strategies: [
{ storage: localStorage, paths: ['login'] }
],
},
getters: {

},
actions: {

}
})

成功创建后，我们便将用户头像存储在了store中，接下来便是将其在组件中使用

2. 组件中调用

Copy





打开浏览器查看:

成功调用！接下来便是最重要的一步，实现登录后头像能够顺利存储在本地。我们这时候选择直接在actions中添加修改数据的操作：

Copy
actions: {
changeHeadShot() {
console.log('数据存储成功');
this.login = require('../assets/images/head.png')
}
}

将我们写好的action在组件中运用：

Copy


<van-col span="8" @click="headerC">
<van-button class="btn2" plain hairlin type="primary" to="/">
登录
</van-button>
</van-col>



//引入store
import { mainStore } from '@/store/user'
const login = mainStore()

//实现点击修改头像的函数
function headerC() {
login.changeHeadShot()
}



/* 省略不必要的代码 */


接下来便是 "见证奇迹" 的时刻：我们点击登录按钮后，头像将会发生改变：

这时候无论我们怎么刷新，头像都不会出现变化了，我们打开控制台点开应用中的存储，可以看见我们的登录头像已经存储在本地的浏览器中:

结语

总而言之，无论你是否之前接触过 Vuex，我都更推荐你使用Pinia，他相比较于 Vuex，有着更好的兼容性，在 Vuex 的基础上去掉了Mutation，让语法更加简练，更符合 Vue3 的Composition api，Vuex 也不会再进行更新，现在已经处于维护状态了，而 Pinia 作为下一代的 Vuex，又有什么理由不去学习和使用呢？