vue3学习 --- pinia

672 阅读2分钟

「这是我参与2022首次更文挑战的第9天,活动详情查看:2022首次更文挑战

pinia是 Vue.js 的轻量级状态管理库,pinia简化了vuex的相关操作过程和相关概念。可以认为是下一代的vuex,也就是vuex5.0

  1. 在pinia中 即支持options api 也支持composition api
  2. 在pinia中没有modules和mutations概念,actions中既可以做同步操作也可以做异步操作
  3. 可以更好的和TypeScript进行兼容
  4. 支持vue devTools
  5. pinia只能被用于vue框架中

L4tN7u.png

从上图中可以看出,使用pinia创建的每一个store其实就是一个个的模块,这些store会被统一挂载到一个root store中,作为它的子模块来进行使用

基本使用

安装

npm install pinia

基本使用

main.ts --- 全局注册pinia

import { createApp } from 'vue'
import { createPinia } from 'pinia'

import App from './App.vue'

// 创建pinia实例
const pinia = createPinia()

const app = createApp(App)

// 注册pinia
app.use(pinia)

app.mount('#app')

store/index.ts

import { defineStore } from 'pinia'

// 定义一个store
// defineStore函数 会返回一个帮助我们使用store的hook函数
// 参数1 --- store的名称 --- 必须唯一
// 参数2 --- 配置对象
export const useUserInfoStore = defineStore('userInfo', {
  // store中的data
  state() {
    return {
      count: 100
    }
  },

  // store中的computed
  getters: { },

  // store中的methods
  actions: { }
})

App.vue --- 使用

<template>
  <div>
    count: {{ userInfoStore.count }}
  </div>
</template>

<script setup lang="ts">
  import { useUserInfoStore } from './store'

  // 调用hook,生成对应的store --- 类型是一个proxy
  const userInfoStore = useUserInfoStore()
</script>

解构

<template>
  <div>
    count: {{ count }}

    <button @click="() => count++">increment</button>
  </div>
</template>

<script setup lang="ts">
  import { storeToRefs } from 'pinia'
  import { useUserInfoStore } from './store'

  const userInfoStore = useUserInfoStore()

  // 默认情况下 pinia对store中的state进行的响应式代理
  // 而解构出来的值并不是响应式的
  // const { count } = userInfoStore

  // 为此 pinia提供了 和vue3中toRefs相同功能的方法 storeToRefs
  // 解构出来的值 都是一个个的ref对象
  const { count } = storeToRefs(userInfoStore)
</script>

状态修改

方式一:

<template>
  <div>
    name: {{ store.name }}
    count: {{ store.count }}

    <button @click="change">change</button>
  </div>
</template>

<script setup lang="ts">
  import { useUserInfoStore } from './store'

  const store = useUserInfoStore()

  // 修改方式一: 分开依次更新
  const change = () => {
    store.name = 'Alex'
    store.count++
  }
</script>

方式二:

<template>
  <div>
    name: {{ store.name }}
    count: {{ store.count }}

    <button @click="change">change</button>
  </div>
</template>

<script setup lang="ts">
  import { useUserInfoStore } from './store'

  const store = useUserInfoStore()

  // 修改方式二: 多个store需要更新的时候,推荐使用批量更新
  const change = () => {
    // 参数可以是对象
    // store.$patch({
    //   name: 'Alex',
    //   count: ++store.count
    // })

    // 参数可以是函数 --- 常见
    store.$patch(state => {
      state.name = 'Alex'
      state.count++
    })
  }
</script>

方式三:

store/index。ts

import { defineStore } from 'pinia'

export const useUserInfoStore = defineStore('userInfo', {
  state() {
    return {
      name: 'Klaus',
      count: 0
    }
  },

  // 在actions中的this就是当前store对象
  actions: {
    // 在这里可以进行参数的传递
    // 注意: 如果此时内部使用了this,那么actions中的函数不可以写成箭头函数
    change(num: number) {
      // 在这里可以分开更新对应的store
      // 也可以使用$patch对多个store进统一更新
      this.$patch(state => {
        state.name = 'Alex'
        state.count += num
      })
    }
  }
})

App.vue

<template>
  <div>
    name: {{ store.name }}
    count: {{ store.count }}

    <button @click="change">change</button>
  </div>
</template>

<script setup lang="ts">
  import { useUserInfoStore } from './store'

  const store = useUserInfoStore()

  // 方式三: 将业务逻辑封装到action中
  const change = () => store.change()
</script>

getters

import { defineStore } from 'pinia'

export const useUserInfoStore = defineStore('userInfo', {
  // state必须写成箭头函数,以便于ts可以正确的进行类型推导
  // 如果写成普通的function,ts可能无法准确的进行类型推导
  state: () => {
    return {
      name: 'Klaus',
      count: 0
    }
  },

  // 和vuex一样这里放置计算属性 --- 有缓存功能
  getters: {
    // state会在这里被传入
    doubleCount(state) {
      return state.count * 2
    }
  },

  actions: {
    change() {
      this.$patch(state => {
        state.name = 'Alex'
        state.count++
      })
    }
  }
})
import { defineStore } from 'pinia'

export const useUserInfoStore = defineStore('userInfo', {
  state: () => {
    return {
      name: 'Klaus',
      count: 0
    }
  },

  getters: {
    // 在getters中我们依旧可以正确获取对应的this
    // 但是此时ts并没有办法正确推导出函数的返回值,所以我们需要手动进行标注
    doubleCount(): number {
      // 在getters中依旧可以使用this获取当前store对象的原因是
      // 方便我们在一个getter函数中,调用另一个getter函数的返回值
      return this.count * 2
    }
  },

  actions: {
    change() {
      this.$patch(state => {
        state.name = 'Alex'
        state.count++
      })
    }
  }
})
import { defineStore } from 'pinia'

export const useUserInfoStore = defineStore('userInfo', {
  state: () => {
    return {
      name: 'Klaus',
      count: 0
    }
  },

  getters: {
    // 也可以在函数形参中加上state参数
    // 目的是为了ts可以正确的推导出函数的返回值
    // 而不需要手动添加对应的类型注解
    doubleCount(state) {
      return this.count * 2
    }
  },

  actions: {
    change() {
      this.$patch(state => {
        state.name = 'Alex'
        state.count++
      })
    }
  }
})