Pinia

2022/2/13 PiniaVueJavaScript

Pinia是Vue的状态管理库,类似Vuex,Vue2/3 都支持。

文章中代码按照vue3写法实现,默认<script lang="ts" setup><script>

Pinia的使用更符合直觉,且支持多个状态库,相比Vuex更简单明了

# 特点

  1. 非常精简
  2. 支持ts,不用做其他操作
  3. 模块化设计,
  4. 没有 mutations,自动记录state变化
  5. 可以扩展插件

# 核心概念与使用

# 引用

import { createPinia } from 'pinia'
const pinia = createPinia()
app.use(pinia)
1
2
3

# Store

保存数据和方法的【仓库】

# 初始化仓库

直接解构会使数据失去响应性

src/store/index

import {defineStore} from 'pinia'

export const mainStore = defineStore('main', {
 state: ()=>{
    return {
      number: 0,
    }
  },
  getters: {},
  actions: {},
})

1
2
3
4
5
6
7
8
9
10
11
12

# 从仓库里取值

xx/xx.vue

<template>
 <div>{{store.number}}</div>
</template>
<script>
 import {mainStore} from '../store/index'
 import {storeToRefs} from 'pinia'
 
 const store = mainStore()

 // 解构赋值
 const { number } = store // Wrong: 直接使用 只会取到第一次赋的值
 const { number } = storeToRefs(store) // Right:正确的解构赋值
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13

# 修改仓库中的值

store的值可以直接修改,也可以用store.$patch来批量赋值

import {mainStore} from '../store/index'
import {storeToRefs} from 'pinia'

const store = mainStore()

// store直接赋值
store.number = 10

// store.$patch批量赋值  优点 批量操作性能比直接赋值强
store.$patch({
 num: 11,
 phone: 13211112222
})

// 处理复杂业务逻辑 也可以传递方法直接修改
store.$patch((state)=>{
 state.num = 12;
})

// action调用方法
// 下面有代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 监听数据

监听store数据的变更

mutations: 事件

state: store对象

// xx/xx.vue
store.$subscribe((mutations, state)=>{
  console.log(mutations, state, state.count);
})
1
2
3
4

# 监听action

监听action的调用情况

name: 调用的action的方法

store: store对象

args: 调用次数

after: action调用完成后回调

onError: 抛出异常

// xx/xx.vue
store.$onAction(({name, store, args, after, onError})=>{
  console.log(name, store, args, after, onError, 'action');
})
1
2
3
4

# Getter

# 读取数据的方法

使用getter中定义的方法读取数据 在数据源未改变时 有缓存机制 不会重复调用,性能由于普通函数计算

// src/store/index
import {defineStore} from 'pinia'
export const mainStore = defineStore('main', {
 state: ()=>{
    return {
      phone: 18612348899,
    }
  },
  getters: {
    phoneFormat():String{
      return this.phone.toString().replace(/^(\d{3})(\d{4})(\d{4})/, '$1-$2-$3')
    }
  },
})

// xx/xx.vue
import {mainStore} from '../store/index'
import {storeToRefs} from 'pinia'
const store = mainStore()
const {phoneFormat} = storeToRefs(store)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# action

操作数据的方法

src/store/index

import {defineStore} from 'pinia'
export const mainStore = defineStore('main', {
 state: ()=>{
    return {
      count: 0,
    }
  },
  actions: {
    changeCount(num: number){
      this.count+=num
    }
  },
})
1
2
3
4
5
6
7
8
9
10
11
12
13

xx/xx.vue

<template>
  <div>
  {{store.count}}
  <button @click="handleClick">handleClick</button>
 </div>
</template>
<script setup lang="ts">
import {mainStore} from '../store/index'
import {storeToRefs} from 'pinia'
const store = mainStore()

// action调用方法 适合有公共数据处理的情况
const handleClick = function(){
  store.changeCount(1)
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# Store的相互调用

// store/goods
import {defineStore} from 'pinia'
export const goodsStore = defineStore('goods', {
  state: ()=>{
    return {
      id: '123123',
      name: '商品名称',
      qty: 0,
    }
  },
})

// store/index
import {defineStore} from 'pinia'
import {goodsStore} from './goods' // 另一个仓库
export const mainStore = defineStore('main', {
  actions: {
    getGoods(){
      const goods = goodsStore()
      return `商品ID:${goods.id};商品名称:${goods.name};加购数量:${goods.qty}`
    }
  },
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 总结

Pinia比Vuex简单很多,而且支持扩展。

Pinia更符合直觉用着更顺手。

TestCode Link (opens new window)