likes
comments
collection
share

Vue3中使用状态管理库Pinia

作者站长头像
站长
· 阅读数 31

前言

Pinia 的一些特点

  • 同时支持 Vue2 和 Vue3
  • 删除了 mutations 相关的操作,只保留 state getters actionsactions中支持同步和异步
  • Pinia中每个 store 都是独立的,不需要再嵌套模块,这样能让代码更简洁
  • 支持 TypeScript
  • 支持服务端渲染

本文记录了 PiniaVue3 中的使用过程,主要包括 state getters actions 三大模块的使用,了解Option StoresSetup Stores 的使用差异。

初始化

先创建一个 Vue3 + TS 的应用,然后再安装 Pinia

使用 Vite 初始化一个 Vue3 的应用,执行命令:

pnpm create vite

输入项目名称,选择 TypeScript 版本,然后进入项目目录、安装依赖、启动项目:

pnpm install
pnpm run dev

查看项目没问题后,再安装 Pinia:

pnpm i pinia

main.ts 中导入并使用 pinia

import { createPinia } from "pinia"

createApp(App).use(pinia).mount('#app')

创建一个 store

项目根目录下新建 store/index.ts 文件,创建一个 store。

import { defineStore } from "pinia"

export const mainStore = defineStore('main', {
  
})

Pinia 使用 defineStore() 来定义一个 store,它的第一个参数是 store 的命名,第二个参数是一个对象,state 等逻辑就在这个对象里面编写。如下是完整结构:

import { defineStore } from "pinia"

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

state

在 store 中创建 state,定义一个 count 返回出去:

export const mainStore = defineStore('main', {
  state: () => {
    return {
      count: 0,
      text: '文字'
    }
  }
})

新建 Count.vue组件,在App.vue中导入使用。在组件中使用 store 里面的 state

<script setup lang="ts">
  import { mainStore } from '../store/index'
  const store = mainStore()
</script>
  
<template>
  <h1>count: {{ store.count }}</h1>
</template>

传统的结构方式,会让数据丢失响应式,Pinia 中解构 state 的值,可以使用 storeToRefs,如下:

  import { storeToRefs } from 'pinia'
  // 失去响应式
  // const { count, text } = store
  // 不会失去响应式
  const { count, text } = storeToRefs(store)
  <p>解构 count:{{ count }}</p>
  <p>解构 text:{{ text }}</p>

getters

getters 类似于Vue中的计算属性,有缓存机制,当它依赖的内容未发生改变时,多次读取 getters 的数据,会去读取缓存的数据,而不会每次读取都重新计算,有利于性能优化。

创建 getters,声明 doubleCount 方法:

export const mainStore = defineStore('main', {
  state: () => {
    return {
      count: 0,
      text: '文字'
    }
  },
  getters: {
    doubleCount(state) {
      console.log('读取doubleCount -------')
      return state.count * 2
    }
  }
})

读取 getters

<h1>getters - doubleCount: {{ store.doubleCount }}</h1>

新建 CountButton.vue 组件,并在App.vue中导入使用:

  function handleAddCount() {
    store.count++
  }
<button @click="handleAddCount">count + 1</button>

点击按钮时,Count.vue组件视图中的count每次+1,doubleCount会翻倍。

actions

actions 中编写一些逻辑,对 state 数据进行处理。

export const mainStore = defineStore('main', {
  state: () => {
    return {
      count: 0,
      // ...
    }
  },
  getters: {
    // ...
  },
  actions: {
    addUser() {
      this.count++
    }
  }
})

调用 actions:

<button @click="handleAddAction">调用action</button>
  function handleAddAction() {
    store.addUser()
  }

$patch 修改状态值

要操作 state 中的值时,上面用到了在 actions 中操作和直接赋值修改 state.count = 10,除了这两种方式以外还可以,使用 $patch 这个API。

<button @click="handlePatch1">$patch修改值</button>
  function handlePatch1() {
    store.$patch({
      count: 99,
      text: store.text === '文字' ? 'text' : '文字'
    })
  }

上面用了 $patch 方法直接修改了 state 的值。如果想要对数组进行增加删除数据或其他一些复杂逻辑,$patch还可以接收一个函数用来处理复杂逻辑。如下:

  function handlePatch2() {
    store.$patch(state => {
      if (state.count > 10) {
        state.count--
      } else if(state.count < 5) {
        state.count++
      } else {
        state.count = 15
      }
    })
  }

虽然有很多方法能够成功修改 state,但因为是全局的状态管理,所以还是尽量放在 actions 中统一进行修改,尽量不在单独的组件处随意修改。这样能使代码结构能够更加合理,可读性更强。

Setup Stores

上面创建了一个完整的 Store,用的是 Option Stores 的方式,类似于 Vue2 的Options API。Option Stores 方式创建 Store 需要将数据、函数固定的放在 state getters actions 中,这种方式不利于更好的组织代码。Setup Stores类似于 Vue3 的 Composition API,能够按照功能逻辑来组织代码、灵活性更高。

创建 store/user.ts,下面使用 Setup Stores 方式创建一个 Store。

import { defineStore } from "pinia"

export const useUserStore = defineStore('user', () => {
  
})

依然是使用 defineStore 方法,不同的地方是第二个参数不再是一个对象,而是一个函数。在这个函数中定义变量、方法等然后再返回出去,就可以在其他组件中使用。

import { defineStore } from "pinia"
import { computed, reactive } from "vue"

type userT = string | number

export const useUserStore = defineStore('user', () => {
  // state
  const count = 1
  let userList: userT[] = reactive([])
  // getters
  const userNum = computed(() => userList.length)
  // actions
  function addUser(userName:userT) {
    userList.push(userName)
  }
  function resetUser() {
    userList.length = 0
  }

  return {
    count,
    userList,
    userNum,
    addUser,
    resetUser
  }
})

新建 UserList.vue 组件,在这个组件中使用 state 和 getters:

  import { useUserStore } from '../store/user'
  const userStore = useUserStore()
  <h3>当前有 {{userStore.userNum}} 个用户 </h3>
  <ul>
    <li v-for="item in userStore.userList">{{ item }}</li>
  </ul>

CountButtton.vue中使用 actions 操作数据:

  function handleAddUser() {
    userStore.addUser('用户')
  }
  function handleResetUser() {
    userStore.resetUser()
  }
  <button @click="handleAddUser">添加用户</button>
  <button @click="handleResetUser">清空用户</button>

代码地址

github.com/kongcodes/l…