安装
yarn add pinia
// 或者使用 npm
npm install pinia
在main.ts中挂载到应用上:
import { createApp } from 'vue'
import { createPinia } from 'pinia' //主要代码
import App from '@/App.vue'
import router from './router'
const app = createApp(App)
app.use(createPinia()) //主要代码
app.use(router)
app.mount('#app')
定义store
注意:action 可以是异步的,你可以在它们里面 await 调用任何 API,以及其他 action
src目录下创建 stores 目录,用于存储 store,如下:
counter.ts:
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
//defineStore():
//第一个参数:独一无二的名字,storeId
//第二个参数:函数或对象
export const useCounterStore = defineStore('counter', () => {
/**
* ref() 就是 state 属性
* computed() 就是 getters
* function() 就是 actions,action中可以使用异步函数
*/
//state:
const count = ref(0)
const name = ref('')
//getter:
const getCount = computed<number>(() => {
return count.value
})
//actions:
const increment = () => {
count.value++
}
const updateName = async (val: string) => {
try {
//demoPromise 是一个 promise
name.value = await demoPromise(val)
} catch (error) {
return error
}
}
//暴露state、computed、actions;否则无法使用
return {count,getCount,increment,updateName }
})
使用Store
- 在
<script setup>
调用useStore()之前,store 实例是不会被创建的 - store是一个用reactive包装的对象,这意味着不需要使用 .value
使用方法:
//在模板中使用:
<template>
<h1>pinia的使用</h1>
<h2>count:{{ store.count }}---{{ store.getCount }}</h2>
<h2>name:{{ store.name }}</h2>
<button @click="store.increment">使用store自带的方法使count递增</button>
</template>
//在<script setup> 中使用:
import { useCounterStore } from '@/stores/counter'
const store = useCounterStore()
const handleIncrement = () => {
//直接更改state,不通过action:
store.count++
//通过action修改state:
increment()
}
解构store
错误的用法:
//count 的响应式被破坏,无论如何更改都不会变,始终保持初始值
const { count } = store
正确的用法:
- 使用 storeToRefs() 不会破坏响应式,它会为每一个响应式属性创建引用
- storeToRefs() 只能解构出 state、getter;不能解构 action
- storeToRefs() 解构出来的state、getter需要使用 .value 获取值
- action 可以直接被解构,无需使用任何工具
const { name, count } = storeToRefs(store)
const { increment } = store
const interval = setInterval(() => {
name.value += '=='
count.value++
}, 2000)
setTimeout(() => {
clearInterval(interval)
}, 10000)
重置state
store.$reset()
变更state
- 方法一:
store.count++
- 方法二:
action
- 方法三:
store.$patch()
$patch(): 可以传递一个函数或对象;主要用于同一事件改变多个属性
const handleUpdate = () => {
store.$patch({
age: 30,
address: '美国',
})
}
const handleUpdateList = () => {
store.$patch(state => {
state.list.push('my')
state.flag = true
})
}
// 实际上并没有替换`$state`
store.$state = { count: 24 }
监听state — $subscribe()
$subscribe()
监听state的变化,通过$patch()
一次改变多个数据,$subscribe()
只会被触发一次- 第一个参数:监听回调
- 第二个参数(可选):配置对象,如
{detached: true}
(此订阅器即便在组件卸载之后仍会被保留)
store.$subscribe(
(mutation, state) => {
/**
* mutation.type:
* 'direct':正常修改,如count++、increment()
* 'patch object':$patch(对象)
* 'patch function':$patch(函数)
*
* mutation.storeId: 使用defineStore()定义store中的id,如:defineStore
*
* mutation.payload:
* 只有 mutation.type === 'patch object'的情况下才可用,$patch() 的补丁对象
*/
console.log(mutation)
},
{
// detached: true, //此订阅器即便在组件卸载之后仍会被保留
}
)
监听 action — $onAction()
- 监听器会被绑定到添加它们的组件上(如果 store 在组件的 setup() 内)
- 当组件被卸载时,它们也会被自动删除
- $onAction中的回调函数会在action 本身之前执行
- 第一个参数:监听回调
- 第二个参数(可选):false – 组件卸载后,监听器自动删除;true — 组件卸载后,监听器仍然保留
let actionCounter = ref(0)
// name: action名称
// store:store实例,类似someStore
// args:传递给action的参数数组
// after:在action 返回或解决后的钩子
// onErro:action 抛出错误或 reject 时执行一个回调函数
const onAction = store.$onAction(({ name, store, args, after, onError }) => {
actionCounter.value++
console.log(name)
console.log(store)
console.log(args)
const startTime = Date.now()
console.log('触发时间:', startTime)
after(result => {
console.log('结束时间:', Date.now())
})
onError(error => {
console.warn('发生错误的时间:', Date.now())
})
})
watch(actionCounter, () => {
// 手动删除监听器
console.log('删除侦听器')
onAction()
})
// 此订阅器即便在组件卸载之后仍会被保留
store.$onAction(() => {}, true)
手动删除监听器:
onAction()