关于Pinia状态管理解读

 更新时间:2023年07月25日 09:40:30   作者:悠然予夏  
这篇文章主要介绍了Pinia状态管理解读,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

1、Pinia和Vuex的对比

1.1、什么是Pinia呢?

Pinia(发音为/piːnjʌ/,如英语中的“peenya”)是最接近piña(西班牙语中的菠萝)的词;

  • Pinia开始于大概2019年,最初是作为一个实验为Vue重新设计状态管理,让它用起来像组合式API(Composition API)。
  • 从那时到现在,最初的设计原则依然是相同的,并且目前同时兼容Vue2、Vue3,也并不要求你使用Composition API;
  • Pinia本质上依然是一个状态管理的库,用于跨组件、页面进行状态共享(这点和Vuex、Redux一样);

1.2、Pinia和Vuex的区别 

那么我们不是已经有Vuex了吗?为什么还要用Pinia呢?

  • Pinia 最初是为了探索 Vuex 的下一次迭代会是什么样子,结合了 Vuex 5 核心团队讨论中的许多想法;
  • 最终,团队意识到Pinia已经实现了Vuex5中大部分内容,所以最终决定用Pinia来替代Vuex;
  • 与 Vuex 相比,Pinia 提供了一个更简单的 API,具有更少的仪式,提供了 Composition-API 风格的 API;
  • 最重要的是,在与 TypeScript 一起使用时具有可靠的类型推断支持;

和Vuex相比,Pinia有很多的优势:

比如mutations 不再存在:

  • 他们经常被认为是非常冗长;
  • 他们最初带来了 devtools 集成,但这不再是问题;

更友好的TypeScript支持,Vuex之前对TS的支持很不友好;

不再有modules的嵌套结构:

  • 你可以灵活使用每一个store,它们是通过扁平化的方式来相互使用的;

也不再有命名空间的概念,不需要记住它们的复杂关系;

1.3、如何使用Pinia? 

使用Pinia之前,我们需要先对其进行安装:

yarn add pinia
# 或者使用 npm
npm install pinia

创建一个pinia并且将其传递给应用程序:

2、创建Pinia的Store 

2.1、认识Store

什么是Store?

  • 一个 Store (如 Pinia)是一个实体,它会持有为绑定到你组件树的状态和业务逻辑,也就是保存了全局的状态;
  • 它有点像始终存在,并且每个人都可以读取和写入的组件;
  • 你可以在你的应用程序中定义任意数量的Store来管理你的状态;

Store有三个核心概念:

  • state、getters、actions;
  • 等同于组件的data、computed、methods;
  • 一旦 store 被实例化,你就可以直接在 store 上访问 state、getters 和 actions 中定义的任何属性;

2.2、定义一个Store

定义一个Store:

我们需要知道 Store 是使用 defineStore() 定义的

并且它需要一个唯一名称,作为第一个参数传递;

这个 name,也称为 id,是必要的,Pinia 使用它来将 store 连接到 devtools。

返回的函数统一使用useX作为命名方案,这是约定的规范;

2.3、使用定义的Store

Store在它被使用之前是不会创建的,我们可以通过调用use函数来使用Store:

注意:Store获取到后不能被解构,那么会失去响应式

为了从 Store 中提取属性同时保持其响应式,您需要使用storeToRefs()。 

2.4、示例代码 

index.js

import {createPinia} from 'pinia'
// 创建pinia
const pinia = createPinia()
export default pinia

counter.js

// 定义关于counter的store
import {defineStore} from 'pinia'
// 参数一为标识名
// 返回值为一个函数
const useCounter = defineStore("counter", {
    state: () => ({
        count: 99
    })
})
export default useCounter

Home.vue

<template>
  <div class="home">
    <h2>Home View</h2>
    <h2>count: {{ counterStore.count }}</h2>
    <h2>count: {{ count }}</h2>
    <button @click="incrementCount">count+1</button>
  </div>
</template>
<script setup>
  import {toRefs} from 'vue'
  import {storeToRefs} from 'pinia'
  import useCounter from '@/stores/counter';
  // 调用函数,拿到store对象
  const counterStore = useCounter()
  // 解构对象(解构出来的对象会失去响应式)
  // const { count } = toRefs(counterStore)
  // storeToRefs这是vue提供的,作用与toRefs相同
  const {count} = storeToRefs(counterStore)
  // 修改数据
  function incrementCount() {
    counterStore.count++
  }
</script>
<style scoped>
</style>
 

App.vue

<template>
  <div class="app">
    <h2>App Component</h2>
    <hr>
    <home/>
  </div>
</template>
<script setup>
  import Home from './views/Home.vue'
</script>
<style>
</style>

main.js

import {createApp} from 'vue'
import App from './App.vue'
import pinia from './stores/index.js'
createApp(App).use(pinia).mount('#app')

注意:index.js、App.vue、main.js接下来都不会发生改变了,所以下面的示例代码就不会写出来了。

3、Pinia核心概念State

3.1、认识和定义State

state 是 store 的核心部分,因为store是用来帮助我们管理状态的。

在 Pinia 中,状态被定义为返回初始状态的函数;

3.2、操作State(一) 

读取和写入 state:

默认情况下,您可以通过 store 实例访问状态来直接读取和写入状态;

重置 State:

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

3.3、操作State(二) 

改变State:

除了直接用 store.counter++ 修改 store,你还可以调用 $patch 方法;

它允许您使用部分“state”对象同时应用多个更改

替换State:

您可以通过将其 $state 属性设置为新对象来替换 Store 的整个状态: 

3.4、代码示例 

Home.vue

<template>
  <div class="home">
    <h2>Home View</h2>
    <h2>name: {{ name }}</h2>
    <h2>age: {{ age }}</h2>
    <h2>level: {{ level }}</h2>
    <button @click="changeState">修改state</button>
    <button @click="resetState">重置state</button>
  </div>
</template>
<script setup>
  import useUser from '@/stores/user'
  import {storeToRefs} from 'pinia';
  const userStore = useUser()
  const {name, age, level} = storeToRefs(userStore)
  function changeState() {
    // 1.一个个修改状态
    // userStore.name = "kobe"
    // userStore.age = 20
    // userStore.level = 200
    // 2.一次性修改多个状态
    // userStore.$patch({
    //   name: "james",
    //   age: 35
    // })
    // 3.替换state为新的对象
    const oldState = userStore.$state
    userStore.$state = {
      name: "curry",
      level: 200
    }
    console.log(oldState === userStore.$state)
  }
  function resetState() {
    userStore.$reset() // 重置state
  }
</script>
<style scoped>
</style>
 

user.js

import {defineStore} from 'pinia'
const useUser = defineStore("user", {
    state: () => ({
        name: "why",
        age: 18,
        level: 100
    })
})
export default useUser

4、Pinia核心概念Getters

4.1、认识和定义Getters

Getters相当于Store的计算属性:

它们可以用 defineStore() 中的 getters 属性定义;

getters中可以定义接受一个state作为参数的函数

4.2、访问Getters(一) 

访问当前store的Getters:

Getters中访问自己的其他Getters:

我们可以通过this来访问到当前store实例的所有其他属性; 

访问其他store的Getters:

4.3、访问Getters(二) 

Getters也可以返回一个函数,这样就可以接受参数:

4.4、代码示例 

counter.js

// 定义关于counter的store
import {defineStore} from 'pinia'
import useUser from './user.js'
const useCounter = defineStore("counter", {
    state: () => ({
        count: 99,
        friends: [
            {id: 111, name: "why"},
            {id: 112, name: "kobe"},
            {id: 113, name: "james"},
        ]
    }),
    getters: {
        // 1.基本使用
        doubleCount(state) {
            return state.count * 2
        },
        // 2.一个getter引入另外一个getter
        doubleCountAddOne() {
            // this是store实例
            return this.doubleCount + 1
        },
        // 3.getters也支持返回一个函数
        getFriendById(state) {
            return function (id) {
                for (let i = 0; i < state.friends.length; i++) {
                    const friend = state.friends[i]
                    if (friend.id === id) {
                        return friend
                    }
                }
            }
        },
        // 4.getters中用到别的store中的数据
        showMessage(state) {
            // 1.获取user信息
            const userStore = useUser()
            // 2.获取自己的信息
            // 3.拼接信息
            return `name:${userStore.name}-count:${state.count}`
        }
    }
})
export default useCounter

Home.vue 

<template>
  <div class="home">
    <h2>Home View</h2>
    <h2>doubleCount: {{ counterStore.doubleCount }}</h2>
    <h2>doubleCountAddOne: {{ counterStore.doubleCountAddOne }}</h2>
    <h2>friend-111: {{ counterStore.getFriendById(111) }}</h2>
    <h2>friend-112: {{ counterStore.getFriendById(112) }}</h2>
    <h2>showMessage: {{ counterStore.showMessage }}</h2>
    <button @click="changeState">修改state</button>
    <button @click="resetState">重置state</button>
  </div>
</template>
<script setup>
  import useCounter from '@/stores/counter';
  const counterStore = useCounter()
</script>
<style scoped>
</style>
 

5、Pinia核心概念Actions

5.1、认识和定义Actions

Actions 相当于组件中的 methods。

可以使用 defineStore() 中的 actions 属性定义,并且它们非常适合定义业务逻辑;

和getters一样,在action中可以通过this访问整个store实例的所有操作; 

5.2、Actions执行异步操作 

并且Actions中是支持异步操作的,并且我们可以编写异步函数,在函数中使用await;

5.3、代码示例 

counter.js 

// 定义关于counter的store
import {defineStore} from 'pinia'
import useUser from './user'
const useCounter = defineStore("counter", {
    state: () => ({
        count: 99,
        friends: [
            {id: 111, name: "why"},
            {id: 112, name: "kobe"},
            {id: 113, name: "james"},
        ]
    }),
    getters: {
        // 1.基本使用
        doubleCount(state) {
            return state.count * 2
        },
        // 2.一个getter引入另外一个getter
        doubleCountAddOne() {
            // this是store实例
            return this.doubleCount + 1
        },
        // 3.getters也支持返回一个函数
        getFriendById(state) {
            return function (id) {
                for (let i = 0; i < state.friends.length; i++) {
                    const friend = state.friends[i]
                    if (friend.id === id) {
                        return friend
                    }
                }
            }
        },
        // 4.getters中用到别的store中的数据
        showMessage(state) {
            // 1.获取user信息
            const userStore = useUser()
            // 2.获取自己的信息
            // 3.拼接信息
            return `name:${userStore.name}-count:${state.count}`
        }
    },
    actions: {
        increment() {
            this.count++
        },
        incrementNum(num) {
            this.count += num
        }
    }
})
export default useCounter

home.js

import {defineStore} from 'pinia'
const useHome = defineStore("home", {
    state: () => ({
        banners: [],
        recommends: []
    }),
    actions: {
        async fetchHomeMultidata() {
            // fetchHomeMultidata() {
            const res = await fetch("http://123.207.32.32:8000/home/multidata")
            const data = await res.json()
            this.banners = data.data.banner.list
            this.recommends = data.data.recommend.list
            // return new Promise(async (resolve, reject) => {
            //   const res = await fetch("http://123.207.32.32:8000/home/multidata")
            //   const data = await res.json()
            //   this.banners = data.data.banner.list
            //   this.recommends = data.data.recommend.list
            //   resolve("bbb")
            // })
        }
    }
})
export default useHome

Home.vue

<template>
  <div class="home">
    <h2>Home View</h2>
    <h2>doubleCount: {{ counterStore.count }}</h2>
    <button @click="changeState">修改state</button>
    <!-- 展示数据 -->
    <h2>轮播的数据</h2>
    <ul>
      <template v-for="item in homeStore.banners">
        <li>{{ item.title }}</li>
      </template>
    </ul>
  </div>
</template>
<script setup>
  import useCounter from '@/stores/counter';
  import useHome from '@/stores/home';
  const counterStore = useCounter()
  function changeState() {
    // counterStore.increment()
    counterStore.incrementNum(10)
  }
  const homeStore = useHome()
  homeStore.fetchHomeMultidata().then(res => {
    console.log("fetchHomeMultidata的action已经完成了:", res)
  })
</script>
<style scoped>
</style>
 

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • elementUI中input回车触发页面刷新问题与解决方法

    elementUI中input回车触发页面刷新问题与解决方法

    这篇文章主要给大家介绍了关于elementUI中input回车触发页面刷新问题与解决方法,文中通过实例代码介绍的非常详细,对大家学习或者使用elementUI具有一定的参考学习价值,需要的朋友可以参考下
    2023-07-07
  • Vue实现点击按钮下载文件的操作代码(后端Java)

    Vue实现点击按钮下载文件的操作代码(后端Java)

    最近项目中需要实现点击按钮下载文件的需求,前端用的vue后端使用的java代码,今天通过本文给大家分享vue点击按钮下载文件的实现代码,需要的朋友参考下吧
    2021-08-08
  • 详解Vue webapp项目通过HBulider打包原生APP

    详解Vue webapp项目通过HBulider打包原生APP

    这篇文章主要介绍了详解Vue webapp项目通过HBulider打包原生APP,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-06-06
  • vue如何动态加载组件详解

    vue如何动态加载组件详解

    组件是Vue.js最强大的功能之一,组件可以扩展HTML元素,封装可重用的代码,下面这篇文章主要给大家介绍了关于vue如何动态加载组件的相关资料,需要的朋友可以参考下
    2022-10-10
  • vue-cli配置环境变量的方法

    vue-cli配置环境变量的方法

    本篇文章主要介绍了vue-cli配置环境变量的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-07-07
  • vue实现导航菜单和编辑文本的示例代码

    vue实现导航菜单和编辑文本的示例代码

    这篇文章主要介绍了vue实现导航菜单和编辑文本功能的方法,文中示例代码非常详细,帮助大家更好的参考和学习,感兴趣的朋友可以了解下
    2020-07-07
  • Vue3 Hooks 模块化抽离示例详解

    Vue3 Hooks 模块化抽离示例详解

    这篇文章主要为大家介绍了Vue3 Hooks 模块化抽离示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • Vue ECharts饼图实现方法详解

    Vue ECharts饼图实现方法详解

    这篇文章主要介绍了在vue.js中,使用echarts组件,创建一个饼图,并且获取饼图的数据和属性,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2022-12-12
  • VUE引入DataV报错解决实战记录

    VUE引入DataV报错解决实战记录

    在使用vue开发大屏时,发现了一个很好用的可视化组件库DataV,下面这篇文章主要给大家介绍了关于VUE引入DataV报错解决的实战记录,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-04-04
  • vue项目完成后如何实现项目优化的示例

    vue项目完成后如何实现项目优化的示例

    本文主要介绍了vue项目完成后如何实现项目优化的示例,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-12-12

最新评论