Pinia快速入门
Pinia简介
官方网站
Pinia是Vue生态里Vuex的代替者,一个全新Vue的状态管理库。
优点
Vue2
和Vue3
都支持,这让我们同时使用Vue2
和Vue3
的小伙伴都能很快上手
- 和 Vuex 对比,取消了
Mutations
操作,只有 state
getters
actions
简化状态库管理
- 完全支持
TypeScript
- 无需再创建各个模块嵌套了,
Vuex
中如果数据过多,通常分模块来进行管理,稍显麻烦,而pinia
中每个store
都是独立的,互相不影响
- 体积非常小,只有
1KB
左右。
pinia
支持插件来扩展自身功能。
- 支持服务端渲染
Pinia基础使用
安装Pinia
安装完成后我们需要将pinia
挂载到Vue
应用中,也就是我们需要创建一个根存储传递给应用程序,简单来说就是创建一个存储数据的数据桶,放到应用程序中去。
修改main.js
,引入pinia
提供的createPinia
方法,创建根存储。
1 2 3 4 5 6 7 8 9 10 11 12 13
|
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");
|
创建Store
Store就是数据仓库的意思,也可以理解为公共组件。只不过该公共组件只存放数据,这些数据我们其它所有的组件都能够访问且可以修改。
方法:
在项目src
目录下新建store
文件夹,用来存放所有store
。例如在该目录下新建user.ts
文件,用来存放与user
相关的store
。
使用pinia
提供的defineStore()
方法来创建一个store
,该store
用来存放我们需要全局使用的数据。
代码:
1 2 3 4 5 6 7 8
|
import { defineStore } from 'pinia'
export const useUsersStore = defineStore('users', { })
|
创建store
很简单,调用pinia
中的defineStore
函数即可,该函数接收两个参数:
name
:一个字符串,必传项,该store
的唯一id
。
options
:一个对象,store
的配置项,比如配置store
内的数据,修改数据的方法等等。
我们可以定义任意数量的store
,因为store
就是一个函数。
使用Store
1 2 3 4 5 6 7
|
<script setup> import { useUsersStore } from "../src/store/user"; const store = useUsersStore(); console.log(store); </script>
|
输出:

添加State
将我们需要存放的数据放在options对象中的state属性内。
代码:
1 2 3 4 5 6 7 8 9
| export const useUsersStore = defineStore("users", { state: () => { return { name: "小猪课堂", age: 25, sex: "男", }; }, });
|
注意,state
接收的是一个箭头函数返回的值,它不能直接接收一个对象
操作State
读取State数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <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); // 或者 使用结构方法 const { name, age, sex } = store; </script>
|
多个组件使用State
和单组件使用State方式一样
修改State数据
如果我们想要修改Store
中的数据,就引入pinia
提供的storeToRefs
函数,将数据变为响应式
,然后直接赋值就可以实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <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 } from "../src/store/user"; import { storeToRefs } from 'pinia'; const store = useUsersStore();
const { name, age, sex } = storeToRefs(store); const changeName = () => { store.name = "张三"; console.log(store); }; </script>
|
重置State
直接调用Store
的$reset()
方法即可
1 2 3 4 5
| <button @click="reset">重置store</button>
const reset = () => { store.$reset(); };
|
当我们点击重置按钮时,store
中的数据会变为初始状态,页面也会更新。
批量更改State中的数据
使用store
的$patch
方法,添加一个批量更改部分数据的方法。
1 2 3 4 5 6 7 8 9
| <button @click="patchStore">批量修改数据</button>
const patchStore = () => { store.$patch({ name: "张三", age: 100, sex: "女", }); };
|
原理:
1 2 3 4
| store.$patch((state) => { state.items.push({ name: 'shoes', quantity: 1 }) state.hasChanged = true })
|
直接替换整个State
pinia
提供了方法让我们直接替换整个state
对象,使用store
的$state
方法。
1
| store.$state = { counter: 666, name: '张三' }
|
这会将我们提前声明的state
替换为新的对象,可能这种场景用得比较少。
getters属性
getters
是defineStore
参数配置项里面的另一个属性。
getter
属性值是一个对象,该对象里面有各种方法。我们可以把getter
想象成Vue
中的计算属性,它的作用就是返回一个新的结果,既然它和Vue
中的计算属性类似,那么它肯定也是会被缓存的,就和computed
一样。
当然我们这里的getter
就是处理state
数据。
添加getter
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| export const useUsersStore = defineStore("users", { state: () => { return { name: "小猪课堂", age: 25, sex: "男", }; }, getters: { getAddAge: (state) => { return state.age + 100; }, }, });
|
我们在配置项参数中添加了getter
属性,该属性对象中定义了一个getAddAge
方法,该方法会默认接收一个state
参数,也就是state
对象,然后该方法返回的是一个新的数据。
使用getter
接下来我们看一下组件中如何使用getter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <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
等属性也可以以此种方式直接在标签上使用,也可以保持响应式。
getter中调用其它getter
前面我们的getAddAge
方法只是简单的使用了state
方法,但是有时候我们需要在这一个getter
方法中调用其它getter
方法,这个时候如何调用呢?
- 其实很简单,我们可以直接在
getter
方法中调用this
,this
指向的便是store
实例,所以理所当然的能够调用到其它getter
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| export const useUsersStore = defineStore("users", { state: () => { return { name: "小猪课堂", age: 25, sex: "男", }; }, getters: { getAddAge: (state) => { return state.age + 100; }, getNameAndAge(): string { return this.name + this.getAddAge; }, }, });
|
细心的小伙伴可能会发现我们这里没有使用箭头函数的形式,这是因为我们在函数内部使用了this
,箭头函数的this
指向问题相信大家都知道吧!所以这里我们没有采用箭头函数的形式。
那么在组件中调用的形式没什么变化,代码如下:
1
| <p>调用其它getter:{{ store.getNameAndAge }}</p>
|
getter传参
既然getter
函数做了一些计算或者处理,那么我们很可能会需要传递参数给getter
函数,但是我们前面说getter
函数就相当于store
的计算属性,和vue
的计算属性差不多,那么我们都知道Vue
中计算属性是不能直接传递参数的,所以我们这里的getter
函数如果要接受参数的话,也是需要做处理的。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| export const useUsersStore = defineStore("users", { state: () => { return { name: "小猪课堂", age: 25, sex: "男", }; }, getters: { getAddAge: (state) => { return (num: number) => state.age + num; }, getNameAndAge(): string { return this.name + this.getAddAge; }, }, });
|
上段代码中我们getter
函数getAddAge
接收了一个参数num
,这种写法其实有点闭包的概念在里面了,相当于我们整体返回了一个新的函数,并且将state
传入了新的函数。
接下来我们在组件中使用,方式很简单,代码如下:
1
| <p>新年龄:{{ store.getAddAge(1100) }}</p>
|
actions属性
前面我们提到的state
和getters
属性都主要是数据层面的,并没有具体的业务逻辑代码,它们两个就和我们组件代码中的data
数据和computed
计算属性一样。
那么,如果我们有业务代码的话,最好就是写在actions
属性里面,该属性就和我们组件代码中的methods
相似,用来放置一些处理业务逻辑的方法。
actions
属性值同样是一个对象,该对象里面也是存储的各种各样的方法,包括同步方法和异步方法。
添加actions
我们可以尝试着添加一个actions
方法,修改user.ts
。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| export const useUsersStore = defineStore("users", { state: () => { return { name: "小猪课堂", age: 25, sex: "男", }; }, getters: { getAddAge: (state) => { return (num: number) => state.age + num; }, getNameAndAge(): string { return this.name + this.getAddAge; }, }, actions: { saveName(name: string) { this.name = name; }, }, });
|
上段代码中我们定义了一个非常简单的actions
方法,在实际场景中,该方法可以是任何逻辑,比如发送请求、存储token
等等。大家把actions
方法当作一个普通的方法即可,特殊之处在于该方法内部的this
指向的是当前store
。
使用actions
使用actions
中的方法也非常简单,比如我们在App.vue
中想要调用该方法。
代码如下:
1 2 3
| const saveName = () => { store.saveName("我是小猪"); };
|
我们点击按钮,直接调用store
中的actions
方法即可。
总结示例代码
main.ts代码:
1 2 3 4 5 6 7 8 9
| 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");
|
user.ts代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import { defineStore } from "pinia";
export const useUsersStore = defineStore("users", { state: () => { return { name: "小猪课堂", age: 25, sex: "男", }; }, getters: { getAddAge: (state) => { return (num: number) => state.age + num; }, getNameAndAge(): string { return this.name + this.getAddAge; }, }, actions: { saveName(name: string) { this.name = name; }, }, });
|
App.vue代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| <template> <img alt="Vue logo" src="./assets/logo.png" /> <p>姓名:{{ name }}</p> <p>年龄:{{ age }}</p> <p>性别:{{ sex }}</p> <p>新年龄:{{ store.getAddAge(1100) }}</p> <p>调用其它getter:{{ store.getNameAndAge }}</p> <button @click="changeName">更改姓名</button> <button @click="reset">重置store</button> <button @click="patchStore">批量修改数据</button> <button @click="saveName">调用aciton</button>
<!-- 子组件 --> <child></child> </template> <script setup lang="ts"> import child from "./child.vue"; import { useUsersStore } from "../src/store/user"; import { storeToRefs } from "pinia"; const store = useUsersStore(); const { name, age, sex } = storeToRefs(store); const changeName = () => { store.name = "张三"; console.log(store); };
const reset = () => { store.$reset(); };
const patchStore = () => { store.$patch({ name: "张三", age: 100, sex: "女", }); };
const saveName = () => { store.saveName("我是小猪"); }; </script>
|
child.vue代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <template> <h1>我是child组件</h1> <p>姓名:{{ name }}</p> <p>年龄:{{ age }}</p> <p>性别:{{ sex }}</p> <button @click="changeName">更改姓名</button> </template> <script setup lang="ts"> import { useUsersStore } from "../src/store/user"; import { storeToRefs } from 'pinia'; const store = useUsersStore(); const { name, age, sex } = storeToRefs(store); const changeName = () => { store.name = "小猪课堂"; }; </script>
|
总结
pinia
的知识点很少,如果你有Vuex
基础,那么学起来更是易如反掌。其实我们更应该关注的是它的函数思想,大家有没有发现我们在Vue3
中的所有东西似乎都可以用一个函数来表示,pinia
也是延续了这种思想。
所以,大家理解这种组合式编程的思想更重要,pinia
无非就是以下3个大点:
当然,本篇文章只是讲解了基础使用部分,但是在实际工作中也能满足大部分需求了。
参考文章:
小猪课堂:大菠萝!这一次彻底搞懂Pinia!(保姆级教程)
东方小月:一文解析Pinia和Vuex,带你全面理解这两个Vue状态管理模式