Pinia的使用

James

Pinia的使用

介绍

Pinia是一个全新的Vue状态管理库,是Vuex的替代品。

  • Vue2Vue3都能支持
  • 抛弃传统的Mutation,只有stategetteraction,简化状态管理库
  • 不需要嵌套模块,符合Vue3Composition API,让代码扁平化;因为在Pinia中每个store都是独立的,互相不影响
  • TypeScript支持
  • 代码简洁,很好的代码自动分割
  • Pinia中的action支持同步和异步
  • Pinia支持插件来扩展自身功能
  • 支持服务端渲染

基本使用

初始化项目npm create vite@latest

安装Pinianpm i pinia

挂载Pinia

// src/main.js
import { createPinia } from 'pinia'

const pinia = createPinia()
app.use(pinia)

Store

创建store

一个Store是一个实体,它持有未绑定到您的组件树的状态和业务逻辑。它托管全局状态。Store是使用defineStore()定义的,并且它需要一个唯一名称,作为第一个参数传递:

  • name:也称为id,必传项,该store的唯一id
  • options:一个对象,store的配置项,比如配置store内的数据,修改数据的方法等
import { defineStore } from 'pinia'

export const useMainStore = defineStore('main', {
    state: () => {
        return {
            msg: 'Hello World!',
            count: 1
        }
    },
    getters: {},
    actions: {}
})

使用store

<script setup>
    import { useMainStore } from '../store/index'
    const store = useMainStore()
</script>
<template>
	<h2>{{ store.msg }}</h2>
</template>

解构store

store是一个用reactive包裹的对象,这意味着不需要在getter之后写.value,但是,就像setup中的props一样,不能对其进行解构。为了从store中提取属性同时保持其响应式,需要使用storeToRefs()。它将为任何响应式属性创建refs

<script setup>
	import { useMainStore } from '../store/index'
    import { storeToRefs } from 'pinia'
    const store = useMainStore()
    const { count } = storeToRefs(store)
</script>
<template>
	<h2>{{ store.msg }}</h2>
	<h3>Count is: {{ count }}</h3>
</template>

重置状态

可以通过调用store上的$reset()方法将状态重置到其初始值。

const store = useMainStore()
store.$reset()

Pinia修改数据状态

简单数据类型

简单数据直接通过在方法中操作store.xxx来修改

const store = useMainStore()
store.count++

多条数据修改

在进行多条数据修改的时候,推荐使用$patch方法,它可以接受两个类型的参数,函数 和 对象

  • $patch + 对象
  • $patch + 函数:通过函数方式去使用的时候,函数接受一个state的参数,state就是store仓库中的state
<script setup>
	import { useMainStore } from '../store/index'
    const store = useMainStore()
    
    // $patch + 对象
    const onObjClick = () => {
        store.$patch({
            count: store.count + 2,
            msg: '对象修改之后的内容'
    	})
    }
    // $patch + 函数
    const onFunClick = () => {
        store.$patch(state => {
            state.count += 2
            state.msg = '函数修改之后的内容'
        })
    }
</script>
<template>
	<h2>store.count:{{ store.count }}</h2>
	<h2>store.msg:{{ store.msg }}</h2>
	<button @click="onObjClick">修改状态($patch + 对象)</button>
	<button @click="onFunClick">修改状态($patch + 函数)</button>
</template>

替换state

可以通过将其$state属性设置为新对象来替换store的整个状态,这种场景比较少见。

store.$state = {
    count: 0,
    msg: '替换之后的内容'
}

Getters

Getter完全等同于Store状态的计算属性。接收state作为第一个参数的箭头函数使用。getter中的值有缓存特性,如果值没有改变,多次使用也只会调用一次。大多数情况下,getter只会依赖state,但是,他们也可能需要使用其他getter,所以我们可以在定义常规函数是通过this访问到整个store的实例,但是需要定义返回类型(在TypeScript中)。这是由于TypeScript中的一个已知限制,并且不会影响使用箭头函数定义的getter,也不会影响不使用thisgetter

export const useMainStore = defineStore('main', {
    state: () => {
        count: 0
    },
    getters: {
        // 自动判断返回类型
        doubleCount(state) {
            console.log('getter被调用')
            return `state.count的值:${state.count * 2}`
        },
        // 返回类型必须明确设置
        doublePlusOne(): number {
            return this.count * 2 + 1
        }
    }
})

getter传递参数

Getters只是computed的幕后场景,因此无法向它们传递任何参数。但是可以从getter返回一个函数用于接收任何参数。

// src/store/index.js
export const useMainStore = defineStore('main', {
    getters: {
        getUserById: state => {
            return userId => {
                state.users.find(user => user.id === userId)
            }
        }
    }
})

在组件中使用

<script setup>
	import {useMainStore} from '../store/index'
    cosnt store = userMainStore()
</script>
<template>
	<h2>User 2: {{ store.getUserById(1) }}</h2>
</template>

再执行此操作时,getter不再缓存,它们只是调用函数。但是可以在getter本身内部缓存一些结果,这并不常见,但应该性能会提高。

// src/store/index.js
export const useMainStore = defineStore('main', {
    getters: {
        getActiveUserById: state => {
            const activeUsers = state.users.filter((user) => user.active)
      		return userId => activeUsers.find((user) => user.id === userId)
        }
    }
})

访问其他storegetter

Pinia中,可以在一个storeimport另外一个store,然后通过调用引入store方法的形式,获取引入store的状态。

// src/store/other.js
export const useOtherStore = defineStore('other', {
    state: () => {
        return {
            otherList: ['other1', 'other2', 'other3']
        }
    }
})

在原store中引入useOtherStore

import {defineStore} from 'pinia'
import {useOtherStore} from './other'

export const useMainStore = defineStore('main', {
    state: () => {
        return {
            count: 0,
    		msg: 'hello'
        }
    },
    getters: {
        getOtherStoreList() {
            const otherStore = useOtherStore()
            return otherStore.otherList
        }
    }
})

Actions

actions相当于组件中methods,他们可以使用defineStore()中的actions属性定义,且非常适合定义业务逻辑。

export const useMainStore = defineStore('main', {
    state: () => {
        return {
            counter: 0,
            userData: null
        }
    },
    actions: {
        // 同步操作
        increment() {
            this.counter++
        },
        randomizeCounter() {
            this.counter = Math.round(100 * Math.random())
        },
        // 异步操作
        async registerUser(login, password) {
          try {
            this.userData = await api.post({ login, password })
            showTooltip(`Welcome back ${this.userData.name}!`)
          } catch (error) {
            showTooltip(error)
            // 让表单组件显示错误
            return error
          }
        }
    }
})

访问其他store操作

import { useAuthStore } from './auth-store'

export const useSettingsStore = defineStore('settings', {
  state: () => ({
    // ...
  }),
  actions: {
    async fetchUserPreferences(preferences) {
      const auth = useAuthStore()
      if (auth.isAuthenticated) {
        this.preferences = await fetchPreferences()
      } else {
        throw new Error('User must be authenticated')
      }
    }
  }
})
上次更新 2023-04-17 01:48:59