全面解析 Pinia:Vue 状态管理的新选择

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 本文深入探讨了 Pinia,作为 Vuex 的替代品,提供了一种更简洁和高效的状态管理方案。文章涵盖了 Pinia 的核心特性,包括支持 Vue2 和 Vue3、TypeScript 支持、无需嵌套模块的设计,以及对同步和异步操作的支持。详细介绍了如何创建和使用 Store,管理状态、Getters 和 Actions,重置状态以及通过 $patch 方法批量更新状态。最后,探讨了如何在不同 Store 之间共享数据和逻辑,为开发者提供了实用的 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')
      }
    }
  }
})
目录
相关文章
|
7月前
|
设计模式 JavaScript 开发者
深度解析Vue中的插槽机制:打开组件设计的无限可能
深度解析Vue中的插槽机制:打开组件设计的无限可能
121 1
|
2月前
|
JavaScript 前端开发 开发者
Vue执行流程及渲染解析
【10月更文挑战第2天】
112 58
|
7月前
|
开发框架 前端开发 开发者
【Uniapp 专栏】Uniapp 的状态管理功能深度解析
【5月更文挑战第13天】Uniapp 的状态管理对于构建复杂跨平台应用至关重要,它包括全局变量、Vuex 风格管理。核心概念有 State、Actions 和 Mutations。通过状态定义、动作设计和突变管理,提高开发效率和代码可维护性。实际案例和与其他框架比较显示了 Uniapp 的优势。理解并有效利用状态管理,能提升应用质量和开发效率。
338 1
【Uniapp 专栏】Uniapp 的状态管理功能深度解析
|
2月前
|
JavaScript 调度
Vue事件总线(EventBus)使用指南:详细解析与实战应用
Vue事件总线(EventBus)使用指南:详细解析与实战应用
93 1
|
2月前
|
JavaScript 前端开发 UED
Vue执行流程及渲染解析
【10月更文挑战第5天】
|
7月前
|
存储 资源调度 JavaScript
阿珊解析Vuex:实现状态管理的利器
阿珊解析Vuex:实现状态管理的利器
|
2月前
|
移动开发 JavaScript 前端开发
Javaweb之Vue路由的详细解析
Vue.js是一款备受欢迎的前端框架,以其简洁的API和组件化开发模式著称。Vue Router作为其官方路由管理器,在构建单页面应用(SPA)时发挥关键作用,通过URL变化管理组件切换,实现无刷新过渡。本文将详细介绍Vue Router的基础概念、主要功能及使用步骤,帮助JavaWeb开发者快速掌握其工作原理及实践应用。
20 1
|
2月前
|
JSON JavaScript 前端开发
Javaweb中Vue指令的详细解析与应用
Vue指令提供了一种高效、声明式的编码方式,使得开发者可以更专注于数据和业务逻辑,而不是DOM操作的细节。通过熟练使用Vue指令,可以极大地提高开发效率和项目的可维护性。
27 3
|
2月前
|
JavaScript
深入解析:JS与Vue中事件委托(事件代理)的高效实现方法
深入解析:JS与Vue中事件委托(事件代理)的高效实现方法
55 0
|
3月前
|
JavaScript 前端开发 UED
Javaweb中Vue指令的详细解析与应用
Vue指令是Vue框架中非常强大的特性之一,它提供了一种简洁、高效的方式来增强HTML元素和组件的功能。通过合理使用这些指令,可以使你的JavaWeb应用更加响应用户的操作,提高交互性和用户体验。而且,通过创建自定义指令,你可以进一步扩展Vue的功能,使其更贴合你的应用需求。
22 1

推荐镜像

更多