249 lines
6.1 KiB
Markdown
Executable File
249 lines
6.1 KiB
Markdown
Executable File
# 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)
|
||
``` |