Pinia 是 Vue 的存储库,允许我们跨组件/页面共享状态
主要:
store:仓库,我们数据都放在 store 里面,这些数据其他所有的组件都可以访问和修改。
import { defineStore } from 'pinia'
// useStore 可以是 useUser、userCart
// 第一个参数是应用程序 store 的唯一 id
export const useStore = defineStore('main', {
// other options...
})
这个 name,也称为 id 是必要的。使用 useXxx
是跨可组合项的约定,符合 hooks
习惯
useStore
在 setup()
中调用,之前不会创建 store:
<script lang="ts" setup>
import { useStore } form '@/stores/counter'
const store = useStore() // store 实例可以在整个模板中使用
</script>
**注意:**对 store 解构,会失去响应式
// 错误的,会破坏响应式
const { name } = useStore()
为了从 Store 中提取属性同时保持响应式,需要使用 storeToRefs()
。它将为任何响应式属性创建 refs。
注意:解构函数时不可以用
storeToRefs
const store = useStore()
const { name, doubleCount } = storeToRefs(store)
import { defineStore } from 'pinia'
export const useStore = defineStore('storeId', {
// 推荐箭头函数形式
state: () => {
return {
// 会自动推断类型,这里定义的是默认值
count: 0,
name: 'xj',
isAdmain: true
}
}
})
默认情况下,可以通过 store
实例访问状态来直接读取和写入
const store = useStore()
store.counter++
可以通过 store 上的 $reset()
方法将状态重置到初始值
const store = useStore()
store.$reset
除了直接用 store.counter++
修改 store,还可以调用 $patch
方法,这个方法允许我们对 store 的多个对象同时修改
store.$patch({
counter: store.counter + 1,
name: 'xjj'
})
$patch
也可以接受一个函数来批量修改集合内部分对象的情况,这样使用数组的 push
等方法修改数据会容易些
store.$patch((state) => {
state.couter ++
state.name = 'xjj'
})
$state
可以将新对象替换 store 的整个状态
store.$state = { counter: 666, name: 'xjj' }
Pinia 的 $subscribe()
和 Vue 的 watch
类似,优点是 subscriptions 只会在 patches 之后触发一次
userStore.$subscribe((mutation, state) => {
mutation.type // 'direct' | 'patch object' |'patch function'
// 与 userStore.$id 相同
mutation.storeId // 'user'
// 仅适用于 mutation.type === 'patch object'
mutation.payload // 补丁对象传递给 userStore.$patch()
// 每当它发生变化时,将整个状态持久化到本地存储
localStorage.setItem('user', JSON.stringify(state))
})
state subscriptions
默认情况下是绑定到添加它的组件上,意味着当组件卸载后,将会被自动删除。如果希望组件被卸载时仍保留它们,可以将 $subscribe
第二个参数设置为 { detached: true }
userStore.$subscribe(cllbackFn, { detached: true })
getters
相当于 Vue 中的 computed
,等同于 store 的计算值
当我们使用
this
来访问 store 的实例时,需要指定返回类型
defineStore('main', {
state: () => {
return {
counter: 0
}
},
getters: {
// 返回类型必须明确
doubleCounter(): number {
return this.counter * 2
},
// 将自动推断为数字
counterAddOne(state): number {
return this.counter + 1
}
}
})
我们可以通过 this
访问其他的 getter。
使用
this
访问其他 getter,不需要特意指定返回值类型
export const useStore = defineStore({
state: () => {
return { counter: 0 }
},
getters: {
doubleCount: (state) => state.counter * 2,
ddCount: () => this,doubleCoun * 2
}
})
getters
本身无法传递参数,但是我们可以返回一个函数,其函数可以接受任意函数
export const useStore = defineStore({
getters: {
getUserById: () => {
return (userId) => {
return state.users.find(user => user.id === userId)
}
}
}
})
在组件中使用:
<script lang="ts" setup>
const store = useStore()
</script>
<template>
<p>User 2:{{ getUserById(2) }}</p>
</template>
使用函数作为返回值,getter 不再缓存,他们只是调用的函数。但是我们可以在 getter 本身内部缓存一些结果,虽然不常见,但是性能更高:
export const useStore = defineStore('main', {
getters: {
getActiveUserById(state) {
const activeUsers = state.users.filter((user) => user.active)
return (userId) => activeUsers.find((user) => user.id === userId)
},
},
})
Action 相当于组件中的 methods。适合定义业务逻辑
export const useStore = defineStore('main', {
state: () => { counter: 0 },
actions: {
increment() {
this.counter ++
},
randomizeCounter() {
this.counter = Math.round(100 * Math.random())
}
}
})
与 getters 一样,操作可以通过
this
访问 store 实例,并提供完整的类型支持。但是有一点不同的是,actions
可以是异步的,例如下面示例
export const useStore = defineStore('main', {
state: () => { weatherData: null },
actions: {
async initWeatherData() {
this.weatherData = await fetch('http://plumbiu.club:8001')
}
}
})
Pinia 插件是一个函数,可以选择返回要添加到 store 的属性,它需要一个可选参数,一个 context:
export function myPiniaPlugin(context) {
context.pinia // 使用 `createPinia()` 创建的 pinia
context.app // 使用 `createApp()` 创建的当前应用程序(仅限 Vue 3)
context.store // 插件正在扩充的 store
context.options // 定义存储的选项对象,传递给 `defineStore()`
}
之后使用 pinia.use
将函数传递给 pinia
:
pinia.use(myPiniaPlugin)
可以用以下方式简单地在插件中返回它们的对象,来为每个 store 添加属性:
pinia.use(() => ({ hello: 'world' }))
也可以直接在 store
上设置属性。
pinia.use(({ store }) => {
store.hello = 'world'
})
**注意:**每个 store 都是用了
reactive
包装,这也是没有使用.value
语法的情况下访问所有的计算属性以及他们为什么是响应式的原因
// src/store/plugins/persistedstate.ts
import type { PiniaPluginContext } from 'pinia'
/**
* 插件方法,有几个模块执行几次
* @param context
*/
export function persistedstate(context: PiniaPluginContext) {
console.log('初始化的时候执行', context.store.$id)
const currentState = JSON.parse(localStorage.gertItem(context.$id))
// 把数据放到 Pinia 对应的模块里面
context.store.$patch(currentState)
/**
* 每次 state 发生变化,都要把它存放在 localStorage 里面
* 两个参数
* 1.当前修改 store 的上下文
* 2.当前修改的状态
*/
context.store.$subscribe((_store, state) => {
localStorage.setItem(_store.storeId, JSON.stringify(state))
}, {
// 组件卸载依赖还在
detached: true
})
}
import { createPinia } from 'pinia'
import { persistedstate } from './plugins/persistedstate'
const pinia = createPinia()
pinia.use(persistedstate)
export default pinia