2025-03-29 14:35:49 +08:00

249 lines
6.1 KiB
Markdown
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# pinia 2.x
[官网](https://pinia.vuejs.org/introduction.html)
**优势**:
1. 适配vue3的composition api特性
2. 可脱离组件使用
3. 支持热更新
4. 体积小(1KB)
5. 支持 TypeScript
6. 支持ssr
7. 进入vue官方库, 接近 Vuex 5.x 的提案
**不足**:
1. devtool 部分功能不支持
**和 vuex3.x/4.x 的比较**:
1. 没有mutation
2. action既可以执行异步方法, 也可以执行同步方法
3. 没有命名空间(或者说每个store都是一个空间, 扁平状态)
4. store是动态的
**本文档只整理vue3 composition api 部份**
## 安装
```bash
yarn add pinia
```
## 简单使用
> store 有三个概念, state, getters, actions, 类似vue2中的data, computed, methods
```ts
// 创建
// @/store/index.ts
import type { App } from 'vue'
import {createPinia} from 'pinia'
const store = createPinia()
export function setupStore(app: App<Element>){
app.use(store)
}
export {store}
```
````ts
// 使用
// @/main.ts
import { createApp } from 'vue'
import {setupStore} from './store'
import App from './App.vue'
const app = createApp(App)
setupStore(app)
app.mount('#app')
````
```ts
// 定义容器
// @/store/modules/app.ts
import { defineStore } from "pinia"
import {store } from '../index'
// 返回值是个函数
export const useAppStore = defineStore({
// 唯一ID可以配合 Vue devtools 使用
id: 'app',
// 必须是函数: 避免服务端渲染时交叉请求污染数据;
// 箭头函数: 为了更好的类型推导
state: (): any => ({
count: 0, // 这些属性都会自动推断类型
}),
// getters有缓存
// getters函数默认接收一个可选参数state, 带上state时能自动推断函数返回, 不带state, 就得声明返回类型
getters: {
countIsEven(): boolean {
return this.count % 2 === 0 // 像vue2使用data一样
}
},
actions: {
// action不能用箭头函数, this问题
countAddOne(): void {
this.count ++
}
}
})
// export const useStore = defineStore('app', {}), 也可以这样传, 第一个参数是id, 是唯一的
// 在setup之外的地方引用
export function useAppStoreWithOut(){
return useAppStore(store)
}
```
```vue
<!--在组件中使用-->
<template>
{{appStore.count}}
{{appStore.countIsEven}}
<button @click="countAddOne">增加</button>
</template>
<script setup lang="ts">
import {useAppStore} from '@/store/modules/app'
const appStore = useAppStore() // 导入的是函数, 执行后得到 reactive, 具有响应性
const countAddOne = ()=>{
// 更改state有三种方式
// 1. 直接修改
appStore.count++ // reactive不用.value
// 2. $patch(), 针对多个更新有性能优化
// 2.1 传对象
appStore.$patch({count: appStore.count + 1})
// 2.2 传函数
appStore.$patch(state => {
state.count++
})
// 3. 调用action, 复杂逻辑推荐使用action
appStore.countAddOne()
}
</script>
```
```ts
// 在组件外使用: 在组件内使用时, pinia已经注册到app实例了, 但在组件外时, 不一定. 所以需要手动传 store
import {useAppStoreWithOut} from '@/store/modules/app'
const appStore = useAppStoreWithOut()
```
**其他写法: 像setup函数一样定义store**
```ts
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
function increment() {
count.value++
}
return { count, increment }
})
```
## storeToRefs()
```ts
// store是一个reactive, 不能解构, 否则会失去响应性. 想保存响应性, 需要使用 storeToRefs()方法
import {useAppStore} from '@/store/modules/app'
import { storeToRefs } from 'pinia'
const appStore = useAppStore()
const {count} = storeToRefs(appStore) // 会自动跳过action和不是响应性的属性
// 当然在computed里返回 appStore.count 也能实现响应性 const isEven2 = computed(()=>{ return appStore.isEven })
// toRef('count', appStore.count) 也能实现响应性
```
## $reset()
```ts
// 用$reset()方法来把state恢复为初始值
const appStore = useAppStore()
store.$reset()
```
## $patch()
```ts
store.$patch({
list: [1, 2, 3, 4, 5],
name: 'Abalam',
}) // 这样写有时太消耗性能, 比如修改数组时还得新建一个数组
// $patch()可以接收一个函数作为参数, 该函数的默认参数是state
store.$patch(state => {
state.list.push(6)
state.name = 'Jan'
})
```
## $state
````ts
// 通过store的$state属性整个替换state对象
store.$state = { counter: 666, name: 'Paimon' }
````
## $subscribe()
```ts
// 用$subscribe()来订阅state的改变
appStore.$subscribe((mutation, state) => {
console.log('mutation', mutation)
/* mutation: {
type: 'direct' | 'patch object' | 'patch function',
storeId: 'app',
} */
console.log('state', state) // 应用举例: state变化时存到localStorage
})
// 默认情况下state侦听会和组件绑定在一起如果store是在组件的setup中。这意味着当组件卸载时侦听会自动被移除。如果你需要在组件被卸载时侦听仍然保持需要给$subscribe()方法传递第二个参数true
appStore.$subscribe(callback, true)
```
```ts
// 当然, 也可以用watch监听整个state
watch(
pinia.state,
(state) => {
// ...
},
{ deep: true }
)
```
## $onAction()
```ts
// 用store.$onAction()方法侦听、订阅action的调用与结果。在action调用前就会调用这个回调。可以在after回调中改变action的返回结果。在onError回调中可以处理错误。
// 应用场景: 在运行时跟踪错误
const ubsubscribe = someStore.$onAction(
({
name,
store,
args,
after,
onError
}) => {
const startTime = Date.now()
// after会在完成调用后执行
after(result => {
console.log(`花费时间: ${Date.now() - startTime} ms`)
})
onError(error => {
console.warn(error)
})
}
)
// 可手动移除监听
ubsubscribe()
// 默认情况下actions的侦听是在组件初始化是加上的如果store是在一个组件的setup中使用。这意味着组件卸载时侦听会自动被移除。如果你想要组件卸载时不移除侦听在组件中调用$onAction时加上第二个参数true
someStore.$onAction(callback, true)
```