Pinia
什么是Pinia
Pinia 起始于 2019 年 11 月左右的一次实验,其目的是设计一个拥有组合式 API 的 Vue 状态管理库
为什么选择Pinia
在pinia没有出现以前,我们都在使用vuex来进行状态管理,而pinia起源于一次探索 Vuex 下一个迭代的实验,因此结合了 Vuex 5 核心团队讨论中的许多想法。最后,我们意识到 Pinia 已经实现了我们在 Vuex 5 中想要的大部分功能,所以决定将其作为新的推荐方案来代替 Vuex。
使用Pinia的好处
- mutation 已被弃用,只有 state getters actions
- 更加支持TS语法
- 也提供了符合组合式 API 风格的 API
- actions支持同步与异步
- vue开发者工具支持pinia
- 能够构建多个stores
- 轻量
使用
使用包管理工具
yarn add pinia
# 或者使用 npm
npm install pinia
在main.js注册
import {
createApp } from 'vue'
import {
createPinia } from 'pinia'
import App from './App.vue'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.mount('#app')
创建仓库
import {
defineStore} from 'pinia';
export default defineStore("main",{
state:()=>{
return{
count:10
}
}
})
在vue中使用
<template>
<div>
商品数量:{
{ store.count }}
</div>
</template>
<script setup>
import useMainStore from './store/index'
const store = useMainStore()
</script>
渲染结果
核心概念
state
export default defineStore("main",{
state:()=>{
return{
count:10
}
}
})
其中main是store的独一无二的名字,这个名字 ,也被用作 id ,是必须传入的, Pinia 将用它来连接 store 和 devtools。为了养成习惯性的用法,将返回的函数命名为 use… 是一个符合组合式函数风格的约定。
defineStore() 的第二个参数可接受两类值:Setup 函数或 Option 对象。
Option Store
与 Vue 的选项式 API 类似,我们也可以传入一个带有 state、actions 与 getters 属性的 Option 对象
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0 }),
getters: {
double: (state) => state.count * 2,
},
actions: {
increment() {
this.count++
},
},
})
Setup Store
也存在另一种定义 store 的可用语法。与 Vue 组合式 API 的 setup 函数 相似,我们可以传入一个函数,该函数定义了一些响应式属性和方法,并且返回一个带有我们想暴露出去的属性和方法的对象。
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
function increment() {
count.value++
}
return {
count, increment }
})
state 是 store 的数据 (data),getters 是 store 的计算属性 (computed),而 actions 则是方法 (methods)。
使用store
<script setup>
import {
useCounterStore } from '@/stores/counter'
// 可以在组件中的任意位置访问 `store` 变量 ✨
const store = useCounterStore()
</script>
为了养成习惯性的用法,将返回的函数命名为 use…
<script setup>
import {
storeToRefs } from 'pinia'
const store = useCounterStore()
const {
name, doubleCount } = storeToRefs(store)
const {
increment } = store
</script>
为了从 store 中提取属性时保持其响应性,你需要使用 storeToRefs()
访问state
const store = useStore()
store.count++
重置state
你可以通过调用 store 的 $reset() 方法将 state 重置为初始值。
const store = useStore()
store.$reset()
变更state
除了用 store.count++ 直接改变 store,你还可以调用 $patch方法。它允许你用一个 state 的补丁对象在同一时间更改多个属性:
store.$patch({
count: store.count + 1,
age: 120,
name: 'DIO',
})
替换state
你不能完全替换掉 store 的 state,因为那样会破坏其响应性。但是,你可以 patch 它
// 这实际上并没有替换`$state`
store.$state = {
count: 24 }
// 在它内部调用 `$patch()`:
store.$patch({
count: 24 })
订阅state
你可以通过 store 的 $subscribe() 方法侦听 state 及其变化。
cartStore.$subscribe((mutation, state) => {
// import { MutationType } from 'pinia'
mutation.type // 'direct' | 'patch object' | 'patch function'
// 和 cartStore.$id 一样
mutation.storeId // 'cart'
// 只有 mutation.type === 'patch object'的情况下才可用
mutation.payload // 传递给 cartStore.$patch() 的补丁对象。
// 每当状态发生变化时,将整个 state 持久化到本地存储。
localStorage.setItem('cart', JSON.stringify(state))
})
getters
getter
Getter 完全等同于 store 的 state 的计算值。可以通过 defineStore() 中的 getters 属性来定义它们。推荐使用箭头函数,并且它将接收 state 作为第一个参数:
export const useStore = defineStore('main', {
state: () => ({
count: 0,
}),
getters: {
doubleCount: (state) => state.count * 2,
},
})
<p>Double count is {
{ store.doubleCount }}</p>
访问其他的getter(使用普通函数)
export const useStore = defineStore('main', {
state: () => ({
count: 0,
}),
getters: {
// 类型是自动推断出来的,因为我们没有使用 `this`
doubleCount: (state) => state.count * 2,
// 这里我们需要自己添加类型(在 JS 中使用 JSDoc)
// 可以用 this 来引用 getter
/**
* 返回 count 的值乘以 2 加 1
*
* @returns {number}
*/
doubleCountPlusOne() {
// 自动补全 ✨
return this.doubleCount + 1
},
},
})
向getter传递参数
Getter 只是幕后的计算属性,所以不可以向它们传递任何参数。不过,你可以从 getter 返回一个函数,该函数可以接受任意参数:
getUserById: (state) => {
return (userId) => state.users.find((user) => user.id === userId)
}
访问其他 store 的 getter
想要使用另一个 store 的 getter 的话,那就直接在 getter 内使用就好:
import {
useOtherStore } from './other-store'
export const useStore = defineStore('main', {
state: () => ({
// ...
}),
getters: {
otherGetter(state) {
const otherStore = useOtherStore()
return state.localData + otherStore.data
},
},
})
actions
Action 相当于组件中的 method。它们可以通过 defineStore() 中的 actions 属性来定义,并且它们也是定义业务逻辑的完美选择。
export const useCounterStore = defineStore('main', {
state: () => ({
count: 0,
}),
actions: {
increment() {
this.count++
},
randomizeCounter() {
this.count = Math.round(100 * Math.random())
},
},
})
类似 getter,action 也可通过 this 访问整个 store 实例,并支持完整的类型标注(以及自动补全✨)。不同的是,action 可以是异步的,你可以在它们里面 await 调用任何 API,以及其他 action!下面是一个使用 Mande 的例子。请注意,你使用什么库并不重要,只要你得到的是一个Promise,你甚至可以 (在浏览器中) 使用原生 fetch 函数: