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

6.1 KiB
Executable File
Raw Blame History

pinia 2.x

官网

优势:

  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 部份

安装

yarn add pinia

简单使用

store 有三个概念, state, getters, actions, 类似vue2中的data, computed, methods

// 创建
// @/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}
// 使用
// @/main.ts
import { createApp } from 'vue'
import {setupStore} from './store'
import App from './App.vue'

const app = createApp(App)
setupStore(app)
app.mount('#app')
// 定义容器
// @/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)
}
<!--在组件中使用-->
<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>
// 在组件外使用: 在组件内使用时, pinia已经注册到app实例了, 但在组件外时, 不一定. 所以需要手动传 store
import {useAppStoreWithOut} from '@/store/modules/app'
const appStore = useAppStoreWithOut()

其他写法: 像setup函数一样定义store

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  function increment() {
    count.value++
  }

  return { count, increment }
})

storeToRefs()

// 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()

// 用$reset()方法来把state恢复为初始值
const appStore = useAppStore()
store.$reset()

$patch()

store.$patch({
  list: [1, 2, 3, 4, 5],
  name: 'Abalam',
}) // 这样写有时太消耗性能, 比如修改数组时还得新建一个数组
// $patch()可以接收一个函数作为参数, 该函数的默认参数是state
store.$patch(state => {
  state.list.push(6)
  state.name = 'Jan'
})

$state

// 通过store的$state属性整个替换state对象
store.$state = { counter: 666, name: 'Paimon' }

$subscribe()

// 用$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)
// 当然, 也可以用watch监听整个state
watch(
  pinia.state,
  (state) => {
    // ...
  },
  { deep: true }
)

$onAction()

// 用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)