文章目录
- Pinia
- 1. 安装
- 2. 引入vue3
- 3. 初始化store仓库
- 4. 修改state
- 5. 解构store
- 6. store中的方法和计算属性(actions、getters)
- 7. API
- 7.1 $reset
- 7.2 $subscribe 和 $onAction
- 8. 插件
- 案例:持久化插件
Pinia
Pinia官方文档
Pinia GitHub地址
1. 安装
yarn add pinia
# 或者使用 npm
npm install pinia
2. 引入vue3
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const store = createPinia()
const app = createApp(App)
app.use(store)
app.mount('#app')
3. 初始化store仓库
定义一个名称枚举(store-name.ts)
export enum Names {
TEST = 'test',
USER = 'user'
}
定义store
import { defineStore, storeToRefs } from 'pinia'
import { Names } from './store-name'
// 你可以对 `defineStore()` 的返回值进行任意命名,但最好使用 store 的名字,同时以 `use` 开头且以 `Store` 结尾。(比如 `useUserStore`,`useCartStore`)
// 第一个参数是你的应用中 Store 的唯一 ID。
// defineStore() 的第二个参数可接受两类值:Setup 函数或 Option 对象。
// options API
export const useTestStore = defineStore(Names.TEST, {
state: () => ({ name: '多多', count: 1 }),
getters: { // computed 修饰的一些值
double: (state) => state.count * 2,
},
actions: { // methods 同步异步 提交state
increment() {
this.count++
},
}
})
interface User {
name: string
age: number
}
// setup
export const useUserStore = defineStore(Names.USER, () => {
// setup中:ref等于state computed等于getters methods等于actions
const name = ref('图图')
const age = ref(18)
const userMsg = computed(() => {
return `我叫${name.value},我${age.value}岁了`
})
const setUser = (user: User) => {
name.value = user.name
age.value = user.age
}
return { name, age, userMsg, setUser }
})
使用store
<template>
<div>USER:{{ user.name }} --- {{ user.age }} --- {{ user.userMsg }}</div>
</template>
<script setup lang="ts">
import { useUserStore } from '@/store'
const user = useUserStore()
</script>
4. 修改state
修改state
的几种方法:
- 直接修改:
user.name = '改变了'
$patch()
:user.$patch({name: 'patch对象改变'})
$patch((state)=>{})
: 好处是可以写修改逻辑,user.$patch((state)=>{ state.name = 'patch工厂函数' })
$state
: 不常用,需要修改全部值,不然ts类型报错,user.$state = {name: 'state改变', age: 18}
action
: 使用store内的方法user.setUser({ name: '多多', age: 20 })
interface User {
name: string
age: number
}
export const useUserStore = defineStore(Names.USER, () => { // setup
const name = ref('图图')
const age = ref(18)
const setUser = (user: User) => {
!!user.name && (name.value = user.name)
!!user.age && (age.value = user.age)
}
return { name, age, userMsg, setUser }
})
5. 解构store
pinia
中,state
相当于reactive
不能直接解构,会失去响应性,pinia提供了 storeToRefs()
使其解构响应性不丢失
import { storeToRefs } from 'pinia';
const user = useUserStore()
const { name, age } = storeToRefs(user)
6. store中的方法和计算属性(actions、getters)
同步异步方法都可以,也可以进行互相调用
interface User {
name?: string
age?: number
}
const asyncData = () => {
return new Promise<User>((resolve, reject) => {
setTimeout(()=>{
resolve({name: '异步', age: 20})
},2000)
})
}
export const useUserStore = defineStore(Names.USER, () => { // setup
// setup中:ref等于state computed等于getters methods等于actions
const name = ref('图图')
const age = ref(18)
const ageMsg = computed<string>(()=>{
return `我${age.value}岁了`
})
const userMsg = computed<string>(() => {
return `我叫${name.value},${ageMsg.value}`
})
// 同步
const setUser = (user: User) => {
!!user.name && (name.value = user.name)
!!user.age && (age.value = user.age)
}
// 异步
const asyncSetUser = async () => {
const res = await asyncData()
setUser(res)
}
return { name, age, userMsg, setUser, asyncSetUser }
})
7. API
7.1 $reset
重置state初始值,注意只在options AP
I有用,setup函数模式报错
const test = useTestStore()
...
test.$reset()
7.2 $subscribe 和 $onAction
$subscribe
:state
改变时会调用此函数
const user = useUserStore()
...
user.$subscribe((args, state) => {
console.log('args, state :>> ', args, state)
},{
detached: true, // 组件销毁也会监听
// 与watch类似
deep: true,
flush: 'post'
})
$onAction
:调用方法时会调用此函数
// 第二个参数为true时,组件销毁也会监听
// 第二个参数为true时,组件销毁也会监听
// args.after 可以清除一些副作用函数
// args 方法传递的参数
user.$onAction((args) => {
args.after(() => {
console.log('after')
})
console.log('args :>> ', args)
}, true)
8. 插件
插件详情
由于有了底层 API 的支持,Pinia store 现在完全支持扩展。以下是你可以扩展的内容:
- 为
store
添加新的属性 - 定义
store
时增加新的选项 - 为
store
增加新的方法 - 包装现有的方法
- 改变甚至取消
action
- 实现副作用,如本地存储
- 仅应用插件于特定
store
插件是通过 pinia.use()
添加到 pinia
实例的。
import { createPinia } from 'pinia'
import { PiniaPluginContext } from 'pinia'
// 在安装此插件后创建的每个 store 中都会添加一个名为 `secret` 的属性。
// 插件可以保存在不同的文件中
// Pinia 插件是一个函数,可以选择性地返回要添加到 store 的属性。它接收一个可选参数,即 context。
function SecretPiniaPlugin(context: PiniaPluginContext) {
context.pinia // 用 `createPinia()` 创建的 pinia。
context.app // 用 `createApp()` 创建的当前应用(仅 Vue 3)。
context.store // 该插件想扩展的 store
context.options // 定义传给 `defineStore()` 的 store 的可选对象。
// ...
return { secret: 'the cake is a lie' }
}
const pinia = createPinia()
// 将该插件交给 Pinia
pinia.use(SecretPiniaPlugin)
// 在另一个文件中
const store = useStore()
store.secret // 'the cake is a lie'
案例:持久化插件
mian.ts
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import persistPlugins from '@/store/plugins/persist'
import { Names } from '@/store/store-name'
const app = createApp()
const store = createPinia()
store.use(persistPlugins({
key: 'wgh',
include: [Names.USER], // 默认为空,全部持久化
storage: sessionStorage // 默认 localStorage
}))
...
app.mount('#app')
persist.ts
import { PiniaPluginContext } from "pinia";
type Options = {
key?: string
storage?: Storage
include?: string[]
}
// 默认key
const __defaultKey__ = 'wdd'
// 存储数据
const setStorage = (key: string, value: any, storage: Storage): void => {
storage.setItem(key, JSON.stringify(value))
}
// 获取数据
const getStorage = (key: string, storage: Storage) => {
const result = storage.getItem(key) ? JSON.parse(storage.getItem(key) as string) : {}
return result
}
// setup 单个state是ref,所以不能直接使用toRaw
export function toRaws<T extends object>(object: T) {
const ret: any = Array.isArray(object) ? new Array(object.length) : {}
for (const key in object) {
ret[key] = toRaw(object[key])
}
return ret
}
// 利用函数柯里化接收用户传参
const persistPlugins = (options: Options) => {
return (context: PiniaPluginContext) => {
const { store } = context
const storage = options?.storage || localStorage
console.log('storage :>> ', storage);
const data = getStorage(`${options?.key ?? __defaultKey__}-${store.$id}`, storage)
store.$subscribe((args, state) => {
if (!options?.include?.length || options.include?.includes(args.storeId)) {
setStorage(`${options?.key ?? __defaultKey__}-${store.$id}`, toRaws(store.$state), storage)
}
})
return {
...data
}
}
}
export default persistPlugins