飞道的博客

Pinia状态管理

785人阅读  评论(0)

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之前,我们需要先对其进行安装:


  
  1. yarn add pinia
  2. # 或者使用 npm
  3. 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


  
  1. import {createPinia} from 'pinia'
  2. // 创建pinia
  3. const pinia = createPinia()
  4. export default pinia

counter.js


  
  1. // 定义关于counter的store
  2. import {defineStore} from 'pinia'
  3. // 参数一为标识名
  4. // 返回值为一个函数
  5. const useCounter = defineStore( "counter", {
  6. state: () => ({
  7. count: 99
  8. })
  9. })
  10. export default useCounter

Home.vue


  
  1. <template>
  2. <div class="home">
  3. <h2>Home View </h2>
  4. <h2>count: {{ counterStore.count }} </h2>
  5. <h2>count: {{ count }} </h2>
  6. <button @click="incrementCount">count+1 </button>
  7. </div>
  8. </template>
  9. <script setup>
  10. import {toRefs} from 'vue'
  11. import {storeToRefs} from 'pinia'
  12. import useCounter from '@/stores/counter';
  13. // 调用函数,拿到store对象
  14. const counterStore = useCounter()
  15. // 解构对象(解构出来的对象会失去响应式)
  16. // const { count } = toRefs(counterStore)
  17. // storeToRefs这是vue提供的,作用与toRefs相同
  18. const {count} = storeToRefs(counterStore)
  19. // 修改数据
  20. function incrementCount( ) {
  21. counterStore. count++
  22. }
  23. </script>
  24. <style scoped>
  25. </style>

App.vue


  
  1. <template>
  2. <div class="app">
  3. <h2>App Component </h2>
  4. <hr>
  5. <home/>
  6. </div>
  7. </template>
  8. <script setup>
  9. import Home from './views/Home.vue'
  10. </script>
  11. <style>
  12. </style>

main.js


  
  1. import {createApp} from 'vue'
  2. import App from './App.vue'
  3. import pinia from './stores/index.js'
  4. 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


  
  1. <template>
  2. <div class="home">
  3. <h2>Home View </h2>
  4. <h2>name: {{ name }} </h2>
  5. <h2>age: {{ age }} </h2>
  6. <h2>level: {{ level }} </h2>
  7. <button @click="changeState">修改state </button>
  8. <button @click="resetState">重置state </button>
  9. </div>
  10. </template>
  11. <script setup>
  12. import useUser from '@/stores/user'
  13. import {storeToRefs} from 'pinia';
  14. const userStore = useUser()
  15. const {name, age, level} = storeToRefs(userStore)
  16. function changeState( ) {
  17. // 1.一个个修改状态
  18. // userStore.name = "kobe"
  19. // userStore.age = 20
  20. // userStore.level = 200
  21. // 2.一次性修改多个状态
  22. // userStore.$patch({
  23. // name: "james",
  24. // age: 35
  25. // })
  26. // 3.替换state为新的对象
  27. const oldState = userStore. $state
  28. userStore. $state = {
  29. name: "curry",
  30. level: 200
  31. }
  32. console. log(oldState === userStore. $state)
  33. }
  34. function resetState( ) {
  35. userStore.$reset() // 重置state
  36. }
  37. </script>
  38. <style scoped>
  39. </style>

user.js


  
  1. import {defineStore} from 'pinia'
  2. const useUser = defineStore( "user", {
  3. state: () => ({
  4. name: "why",
  5. age: 18,
  6. level: 100
  7. })
  8. })
  9. 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


  
  1. // 定义关于counter的store
  2. import {defineStore} from 'pinia'
  3. import useUser from './user.js'
  4. const useCounter = defineStore( "counter", {
  5. state: () => ({
  6. count: 99,
  7. friends: [
  8. { id: 111, name: "why"},
  9. { id: 112, name: "kobe"},
  10. { id: 113, name: "james"},
  11. ]
  12. }),
  13. getters: {
  14. // 1.基本使用
  15. doubleCount( state) {
  16. return state. count * 2
  17. },
  18. // 2.一个getter引入另外一个getter
  19. doubleCountAddOne( ) {
  20. // this是store实例
  21. return this. doubleCount + 1
  22. },
  23. // 3.getters也支持返回一个函数
  24. getFriendById( state) {
  25. return function ( id) {
  26. for ( let i = 0; i < state. friends. length; i++) {
  27. const friend = state. friends[i]
  28. if (friend. id === id) {
  29. return friend
  30. }
  31. }
  32. }
  33. },
  34. // 4.getters中用到别的store中的数据
  35. showMessage( state) {
  36. // 1.获取user信息
  37. const userStore = useUser()
  38. // 2.获取自己的信息
  39. // 3.拼接信息
  40. return `name:${userStore.name}-count:${state.count}`
  41. }
  42. }
  43. })
  44. export default useCounter

Home.vue 


  
  1. <template>
  2. <div class="home">
  3. <h2>Home View </h2>
  4. <h2>doubleCount: {{ counterStore.doubleCount }} </h2>
  5. <h2>doubleCountAddOne: {{ counterStore.doubleCountAddOne }} </h2>
  6. <h2>friend-111: {{ counterStore.getFriendById(111) }} </h2>
  7. <h2>friend-112: {{ counterStore.getFriendById(112) }} </h2>
  8. <h2>showMessage: {{ counterStore.showMessage }} </h2>
  9. <button @click="changeState">修改state </button>
  10. <button @click="resetState">重置state </button>
  11. </div>
  12. </template>
  13. <script setup>
  14. import useCounter from '@/stores/counter';
  15. const counterStore = useCounter()
  16. </script>
  17. <style scoped>
  18. </style>

5、Pinia核心概念Actions

5.1、认识和定义Actions

Actions 相当于组件中的 methods。

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

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

5.2、Actions执行异步操作 

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

5.3、代码示例 

counter.js 


  
  1. // 定义关于counter的store
  2. import {defineStore} from 'pinia'
  3. import useUser from './user'
  4. const useCounter = defineStore( "counter", {
  5. state: () => ({
  6. count: 99,
  7. friends: [
  8. { id: 111, name: "why"},
  9. { id: 112, name: "kobe"},
  10. { id: 113, name: "james"},
  11. ]
  12. }),
  13. getters: {
  14. // 1.基本使用
  15. doubleCount( state) {
  16. return state. count * 2
  17. },
  18. // 2.一个getter引入另外一个getter
  19. doubleCountAddOne( ) {
  20. // this是store实例
  21. return this. doubleCount + 1
  22. },
  23. // 3.getters也支持返回一个函数
  24. getFriendById( state) {
  25. return function ( id) {
  26. for ( let i = 0; i < state. friends. length; i++) {
  27. const friend = state. friends[i]
  28. if (friend. id === id) {
  29. return friend
  30. }
  31. }
  32. }
  33. },
  34. // 4.getters中用到别的store中的数据
  35. showMessage( state) {
  36. // 1.获取user信息
  37. const userStore = useUser()
  38. // 2.获取自己的信息
  39. // 3.拼接信息
  40. return `name:${userStore.name}-count:${state.count}`
  41. }
  42. },
  43. actions: {
  44. increment( ) {
  45. this. count++
  46. },
  47. incrementNum( num) {
  48. this. count += num
  49. }
  50. }
  51. })
  52. export default useCounter

home.js


  
  1. import {defineStore} from 'pinia'
  2. const useHome = defineStore( "home", {
  3. state: () => ({
  4. banners: [],
  5. recommends: []
  6. }),
  7. actions: {
  8. async fetchHomeMultidata( ) {
  9. // fetchHomeMultidata() {
  10. const res = await fetch( "http://123.207.32.32:8000/home/multidata")
  11. const data = await res. json()
  12. this. banners = data. data. banner. list
  13. this. recommends = data. data. recommend. list
  14. // return new Promise(async (resolve, reject) => {
  15. // const res = await fetch("http://123.207.32.32:8000/home/multidata")
  16. // const data = await res.json()
  17. // this.banners = data.data.banner.list
  18. // this.recommends = data.data.recommend.list
  19. // resolve("bbb")
  20. // })
  21. }
  22. }
  23. })
  24. export default useHome

Home.vue


  
  1. <template>
  2. <div class="home">
  3. <h2>Home View </h2>
  4. <h2>doubleCount: {{ counterStore.count }} </h2>
  5. <button @click="changeState">修改state </button>
  6. <!-- 展示数据 -->
  7. <h2>轮播的数据 </h2>
  8. <ul>
  9. <template v-for="item in homeStore.banners">
  10. <li>{{ item.title }} </li>
  11. </template>
  12. </ul>
  13. </div>
  14. </template>
  15. <script setup>
  16. import useCounter from '@/stores/counter';
  17. import useHome from '@/stores/home';
  18. const counterStore = useCounter()
  19. function changeState( ) {
  20. // counterStore.increment()
  21. counterStore. incrementNum( 10)
  22. }
  23. const homeStore = useHome()
  24. homeStore. fetchHomeMultidata(). then( res => {
  25. console. log( "fetchHomeMultidata的action已经完成了:", res)
  26. })
  27. </script>
  28. <style scoped>
  29. </style>

转载:https://blog.csdn.net/weixin_52851967/article/details/128764471
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场