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

简介: 本文深入探讨了 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')
      }
    }
  }
})
目录
相关文章
|
5月前
|
JavaScript
Vue中如何实现兄弟组件之间的通信
在Vue中,兄弟组件可通过父组件中转、事件总线、Vuex/Pinia或provide/inject实现通信。小型项目推荐父组件中转或事件总线,大型项目建议使用Pinia等状态管理工具,确保数据流清晰可控,避免内存泄漏。
481 2
|
4月前
|
缓存 JavaScript
vue中的keep-alive问题(2)
vue中的keep-alive问题(2)
406 137
|
8月前
|
人工智能 JavaScript 算法
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
938 0
|
8月前
|
JavaScript UED
用组件懒加载优化Vue应用性能
用组件懒加载优化Vue应用性能
|
9月前
|
JavaScript 数据可视化 前端开发
基于 Vue 与 D3 的可拖拽拓扑图技术方案及应用案例解析
本文介绍了基于Vue和D3实现可拖拽拓扑图的技术方案与应用实例。通过Vue构建用户界面和交互逻辑,结合D3强大的数据可视化能力,实现了力导向布局、节点拖拽、交互事件等功能。文章详细讲解了数据模型设计、拖拽功能实现、组件封装及高级扩展(如节点类型定制、连接样式优化等),并提供了性能优化方案以应对大数据量场景。最终,展示了基础网络拓扑、实时更新拓扑等应用实例,为开发者提供了一套完整的实现思路和实践经验。
1183 78
|
7月前
|
人工智能 JSON JavaScript
VTJ.PRO 首发 MasterGo 设计智能识别引擎,秒级生成 Vue 代码
VTJ.PRO发布「AI MasterGo设计稿识别引擎」,成为全球首个支持解析MasterGo原生JSON文件并自动生成Vue组件的AI工具。通过双引擎架构,实现设计到代码全流程自动化,效率提升300%,助力企业降本增效,引领“设计即生产”新时代。
554 1
|
7月前
|
JavaScript 安全
在 Vue 中,如何在回调函数中正确使用 this?
在 Vue 中,如何在回调函数中正确使用 this?
369 0
|
8月前
|
JavaScript 前端开发 开发者
Vue 自定义进度条组件封装及使用方法详解
这是一篇关于自定义进度条组件的使用指南和开发文档。文章详细介绍了如何在Vue项目中引入、注册并使用该组件,包括基础与高级示例。组件支持分段配置(如颜色、文本)、动画效果及超出进度提示等功能。同时提供了完整的代码实现,支持全局注册,并提出了优化建议,如主题支持、响应式设计等,帮助开发者更灵活地集成和定制进度条组件。资源链接已提供,适合前端开发者参考学习。
571 17
|
8月前
|
JavaScript 前端开发 UED
Vue 手风琴实现的三种常用方式及长尾关键词解析
手风琴效果是Vue开发中常见的交互组件,可节省页面空间、提升用户体验。本文介绍三种实现方式:1) 原生Vue结合数据绑定与CSS动画;2) 使用Element UI等组件库快速构建;3) 自定义指令操作DOM实现独特效果。每种方式适用于不同场景,可根据项目需求选择。示例包括产品特性页、后台菜单及FAQ展示,灵活满足多样需求。附代码示例与资源链接,助你高效实现手风琴功能。
437 10

热门文章

最新文章

推荐镜像

更多
  • DNS