Pinia 快速入门(使用教程)
# Pinia 是什么?
Pinia 是一个用于 Vue 的状态管理库,类似 Vuex, 是 Vue 的另一种状态管理方案,支持 Vue2 和 Vue3。
- GitHub地址:https://github.com/vuejs/pinia (opens new window)
- 官方文档:https://pinia.vuejs.org/ (opens new window)
# pinia的优势
- vue2/3都可以用。
- pinia中只有state、getter、action,抛弃了Vuex中的Mutation,用法更简单了。
- pinia 中 action支持同步和异步
- pinia 不用在使用模块(module)对store进行分割。
- pinia 支持插件来扩展自身功能。
- 支持服务端渲染 (SSR)
# 安装pinia
yarn add pinia
# 或者使用 npm
npm install pinia
2
3
# 引入到项目
安装完成后,就可以将pinia引入到项目中了,只需要调用createPinia()
方法将pinia实例化,然后挂载到vue实例上就可以了。
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const app = createApp(App)
const pinia = createPinia()
app.use(pinia)
app.mount('#app')
2
3
4
5
6
7
8
9
10
# 创建store
下一步就应该去创建我们的数据仓库store了,store中存储的是我们需要共享的数据。
创建store需要调用pinia中的defineStore方法,该方法接受两个参数,第一个参数是store的唯一 id,第二个参数是store的配置属性。
defineStore方法配置store属性时,有两种写法,一种是Option 对象形式,一种是Setup 函数形式。
# 对象形式(Option Store)
与 Vue 的选项式 API 类似,我们需要传入一个带有 state、actions 与 getters 属性的 Option 对象。
与vue组件相比,你可以认为 state 是 store 的数据 (data),getters 是 store 的计算属性 (computed),而 actions 则是方法 (methods)。
这种方式非常直观,推荐使用。
// src/store/user.js
import { defineStore } from "pinia"
export const useUserStore = defineStore("user", {
state: () => ({
name: "石头",
age: 18,
}),
getters: {
getPerson: (state) => {
return `${state.name}今年${state.age}岁了`
}
},
actions: {
changeName(name) {
const that = this;
setTimeout(() => {
that.name = name;
}, 1000)
},
},
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 函数形式(Setup Store)
与 Vue 的组合式 API 类似,你可以认为 state 是一个 ref,getters 是一个计算属性 (computed),而 actions 是一个方法 (methods)。
在 Setup Store 中:
- ref() 就是 state 属性
- computed() 就是 getters
- function() 就是 actions
Setup store 比 Option Store 带来了更多的灵活性,因为你可以在一个 store 内创建侦听器,并自由地使用任何组合式函数。
注意
使用组合式函数会让 SSR 变得更加复杂。
import { defineStore } from "pinia"
import {computed, ref} from "vue";
export const useThemeStore = defineStore("theme", ()=>{
const defaultSize = ref(16)
const getBigSize = computed(() => {
return defaultSize.value + 10
})
const changeSize = (size) => {
defaultSize.value = size
}
return {
defaultSize,
getBigSize,
changeSize
}
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 组件内使用
引入上面创建的useUserStore,调用useUserStore方法得到仓库userStore实例。 可以直接使用userStore访问仓库中的属性。
# 对象形式(Option Store)
import { useUserStore } from "@/store/user"
const userStore = useUserStore()
console.log('user',userStore)
<template>
<span>{{userStore.name}}</span>
<span>{{userStore.age}}</span>
</template>
2
3
4
5
6
7
8
9
# 函数形式(Setup Store)
import { useThemeStore } from "@/store/theme"
const themeStore = useThemeStore()
console.log('theme',themeStore)
2
3
现在我们已经学会如何使用pinia了,但pinia中还有几个概念state、getter、action需要我们继续深入了解。
# 仓库state
state是仓库的原始数据,它是一个对象,你可以直接修改state的属性。
export const useUsersStore = defineStore("users", {
state: () => {
return {
name: "小白菜",
age: 25,
sex: "男",
};
},
});
2
3
4
5
6
7
8
9
我们还需要知道state中的数据是如何读取和修改的,又有哪些需要注意的地方。
# 读取state
<script setup>
import { useUsersStore } from '@/stores/user'
const userStore = useUsersStore()
</script>
<template>
<div>
{{userStore.name}}
</div>
</template>
2
3
4
5
6
7
8
9
# 修改 state
方式一:直接修改
userStore.name = 'xxx'
方式二:$patch({}) 对象形式 批量修改
$patch
是 pinia 提供的一个api,用来批量修改 state 中的数据,接收一个对象参数,对象中的属性就是 state 中的属性,值就是要修改的值。
可修改单个或多个属性,pinia内部做了优化,相同属性会被自动覆盖。
userStore.$patch({
name: '土豆粉',
age:19
})
2
3
4
方式三:$patch(fn) 函数形式 批量修改
这是$patch
的另一种写法,回调默认接收一个参数,也就是store中的state,在回调函数中修改state中的数据。 推荐使用
userStore.$patch((state) => {
state.name = '小白菜'
state.age = 90
})
2
3
4
# 监听state
import { watch } from 'vue';
// 监听pinia中整个state
userStore.$subscribe((mutation, state) => {
console.log(state);
})
// 监听pinia中整个state
watch(userStore.$state, (newValue, oldValue) => {
console.log(newValue,oldValue);
})
// 只监听pinia中某一个值的变化
watch(() => userStore.count, (newValue, oldValue) => {
console.log(newValue, oldValue);
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 仓库getters
getters 与Vue中的计算属性 computed 一样,就是仓库中的计算属性,它可以帮助我们快速获取仓库中的数据,并且可以对数据进行加工处理。
注意
- getters是一个对象
- 默认接收一个state参数,就是我们仓库中的state,该参数是可选的。
- 普通函数,可以使用 this 访问整个 Store 的实例
# 基本使用
export const useUserStore = defineStore('user', {
state: () => ({
name: '小白菜',
age: 18,
}),
getters: {
getUser() {
return `${this.name},今年${this.age}岁`
},
getUser2: (state) => {
return `${state.name},今年${state.age}岁`
}
},
})
2
3
4
5
6
7
8
9
10
11
12
13
14
调用
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
console.log('getUser', userStore.getUser)
console.log('getUser2', userStore.getUser2)
2
3
4
# 使用其他getter
我们也可以在某个getter中使用其他的getter,这样就可以实现getter的复用了。
只需要将箭头函数转换为普通函数,函数内部通过 this 来调用当前 Store 上的数据和方法。
export const useStore = defineStore('main', {
state: () => ({
message: 'Hello World',
}),
getters: {
fullMessage: (state) => `The message is "${state.message}".`,
// 这个 getter 返回了另外一个 getter 的结果
emojiMessage() {
return ` ${this.fullMessage}`
},
},
})
2
3
4
5
6
7
8
9
10
11
12
# 给getter传参
我们还可以给getter传递参数,但getter 本身是不支持参数,需要返回一个带有入参的函数,实现给getter传参的效果。
注意
这样 Getter 将不再被缓存,只是个被调用的函数。
import { defineStore } from 'pinia'
export const useStore = defineStore('main', {
state: () => ({
message: 'Hello World',
}),
getters: {
// 定义一个接收入参的函数作为返回值
signedMessage: (state) => {
return (newName) => `${newName} say: "The message is ${state.message}".`
},
},
})
2
3
4
5
6
7
8
9
10
11
12
13
调用
const store = useStore()
const signedMessage = store.signedMessage('Petter')
console.log('signedMessage', signedMessage)
// Petter say: "The message is Hello World".
2
3
4
5
6
# 仓库actions
actions属性就和我们组件代码中的methods相似,用来放置一些处理业务逻辑的方法。
actions属性值同样是一个对象,该对象里面也是存储的各种各样的方法,包括同步方法和异步方法。
这里不在赘述,详细请移步 官方文档Actions (opens new window)
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => {
return {
name: 'hello',
}
},
getters: {},
actions: {
// 同步更新 name 值
updateNameSync(newName) {
this.name = newName
return '同步修改state中的name值完成'
},
// 异步更新 name 值
async updateName(newName) {
return new Promise((resolve) => {
setTimeout(() => {
// 这里的 this 是当前的 Store 实例
this.name = newName
resolve('异步修改完成')
}, 3000)
})
},
},
})
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
调用
在 Pinia 像普通的函数一样使用即可,不需要和 Vuex 一样执行 commit 或者 dispatch,
import { useUserStore } from '@/store/user'
const store = useUserStore()
// 立即执行
store.updateMessageSync('同步')
// 3s 后执行
store.updateMessage('异步')
2
3
4
5
6
7
8
9
# 重置
那我们想重置state中的数据怎么办呢?
$reset()
是pinia提供的一个api,用来重置state中的数据,将state中的数据重置为初始值。
// 通过调用 store 的 $reset() 方法将 state 重置为初始值。
userStore.$reset()
2
# 解构
store使用过程中,还有另外一个用法就是解构,我们可以把store中的state进行解构使用。
但是解构会失去响应式,需要 StoreToRefs 避免丢失响应式。
<script setup>
import { useUsersStore } from '@/stores/user'
const userStore = useUsersStore()
const {name} = userStore
</script>
<template>
<div class="greetings">
{{userStore.name}}
{{name}}
</div>
</template>
2
3
4
5
6
7
8
9
10
11
12
State 也可以使用解构,但是解构会失去响应式,需要 StoreToRefs 避免丢失响应式
import { storeToRefs } from 'pinia'
const userStore = useUsersStore()
const { name } = storeToRefs(userStore)
2
3
# 其他实例api
上面已经介绍了$patch、$reset两个实例api,还有几个实例api不常用,我想我们应该了解一下。
# $subscribe
当state中的值任意一个发生变化的时候,就会触发该函数
/**
* 当state中的值任意一个发生变化的时候,就会触发该函数
*
* args: 里面会记录新旧值
* state:就是当前操作的state的实例
* options: 是一个对象,比如detached,这是一个boolean参数,当这个参数为true时,表明即使当前组件销毁后,也继续监控state里面值的变化,可选
*/
goodsStore.$subscribe((args, state) => {
console.log('args', args)
console.log('state', state)
},{
detached: true
})
2
3
4
5
6
7
8
9
10
11
12
13
# $onAction
当调用actions里面的函数的时候,就会触发该函数
/**
* 当调用actions里面的函数的时候,就会触发改函数
*
* args:接收参数,里面封装了多个api:
* args.after:当$onAction里面的逻辑执行完成之后才会执行args.after函数逻辑,所以args.after放置的位置于执行顺序无关
* args.onError:当调用actions里面的函数发生错误时,args.onError函数也会执行
* args.args:接收调用actions里面的函数传递的参数,是一个数组
* args.name:执行的actions里面的函数的名称
* detached: 这是一个boolean参数,当这个参数为true时,表明即使当前组件销毁时,也继续监控actions里面的函数调用,可选
*/
goodsStore.$onAction((args) => {
args.after(() => console.log("args.after", "===="));
console.log("args", args);
}, true);
2
3
4
5
6
7
8
9
10
11
12
13
14
# 数据持久化
store中的数据,刷新页面后就丢失了,如果想保留这些数据,就要用到数据持久化了。推荐使用 pinia-plugin-persistedstate (opens new window) 插件。
# 安装插件
yarn add pinia-plugin-persistedstate
# or
yarn add pinia-plugin-persistedstate
2
3
# 将插件添加到pinia
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
2
3
4
5
# 开启数据持久化
在仓库中指定persist
为true
,就可以开启数据持久化了。
开启后默认有以下配置:
- 使用 localStorage 进行存储
- store.$id 作为 storage 默认的 key
- 使用 JSON.stringify/JSON.parse 进行序列化/反序列化
- 整个 state 默认将被持久化
下面是Option Store写法,Setup Store 大同小异,去看官方文档吧。
import { defineStore } from 'pinia'
export const useStore = defineStore('main', {
state: () => {
return {
someState: '你好 pinia',
}
},
persist: true,
})
2
3
4
5
6
7
8
9
10
# 其他操作
除上面默认配置外,我们还可以进行一些其他的配置:
- 指定存储的key
- 指定要持久化的数据
- 指定持久化的存储方式,默认 localStorage
import { defineStore } from 'pinia'
export const useStore = defineStore('store', {
state: () => ({
user: {
name: '小白菜',
age: 18,
},
company: '腾讯',
department: '前端',
}),
persist: {
// 指定持久化的key
key: 'my-custom-key',
// 指定要持久化的数据
paths: ['user.name', 'company'],
// 指定持久化的存储方式,默认 localStorage
storage: sessionStorage,
},
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
完结撒花 ,你又进步了一点点。
- 01
- linux 在没有 sudo 权限下安装 Ollama 框架12-23
- 02
- Express 与 vue3 使用 sse 实现消息推送(长连接)12-20