首页 > 其他分享 >Vue全家桶 - pinia 的理解和学习1(Pinia 核心概念的 Store、State、Getter、Action)

Vue全家桶 - pinia 的理解和学习1(Pinia 核心概念的 Store、State、Getter、Action)

时间:2024-07-24 21:55:59浏览次数:15  
标签:count useMyPiniaStore Vue const Pinia state State pinia store

Pinia(Vue 的专属状态管理库)

Pinia 和 Vuex 的区别

设计理念和架构
Vuex 采用集中式架构,所有状态存储在一个全局状态树中,通过 mutationsactions 来修改和处理状态。
Pinia 采用去中心化的架构,每个模块有自己的状态,这使得 Pinia 在代码分割和模块化方面更加灵活。
TypeScript支持
Vuex 早期版本对 TypeScript 支持较弱,需要额外插件实现类型检查。
Pinia 从设计之初就考虑到了 TypeScript,提供了更好的类型推导和检查支持。‌
体积和性能
Pinia的体积较小,加载速度快,且因其轻量级设计,性能优于Vuex。这得益于Pinia充分利用了Vue 3的响应式系统优化。‌
使用难度
Pinia 的API设计更简洁,没有 Vuex 的 mutationsmodules 概念,这使得 Pinia 更容易理解和使用。Pinia 更适合初学者和快速开发项目,而 Vuex 则更适合复杂的项目和对状态管理有更高要求的开发者。‌
社区支持和生态系统
Vuex 作为 Vue.js 官方出品的状态管理库,拥有更强的社区支持和丰富的文档。而 Pinia 虽然是一个较新的库,但在 Vue 3 项目中因其轻量级和简洁性而受到青睐。
总结来说,选择Pinia还是Vuex取决于项目的具体需求和开发者的个人偏好。对于需要快速开发和维护简单性的项目,Pinia可能是更好的选择;而对于需要高度模块化和集成的大型复杂项目,Vuex可能更适合。

Vuex 的理解和学习 https://blog.csdn.net/weixin_54092687/article/details/140280584
Pinia 官方文档 https://pinia.vuejs.org/zh/
进一步学习Pinia的其他核心概念和服务器渲染 https://blog.csdn.net/weixin_54092687/article/details/140561225

定义 Store

// 第一步 -- src/store/store.ts 定义仓库
import { defineStore } from 'pinia'
export const useMyPiniaStore = defineStore('myPinia', { ... });

// 第二步 -- main.ts 注册 Pinia
import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia'
const pinia = createPinia();
createApp(App).use(pinia).mount('#app')

// 第三步 -- 组件中使用Store
<script setup>
import { useMyPiniaStore } from '@/store/store'
const store = useMyPiniaStore()
</script>

Store 参数

第一个参数 - - - id
仓库名称,必填,唯一性,习惯格式 use__Store
第二个参数 - - - 接受两种类型
Option 对象:带有 state | getters | actions 属性。
Setup 函数:ref() => state | computed() => getters | function() => actions

Setup Store

  1. 要让 pinia 正确识别 state,你必须在 setup store 中返回 state 的所有属性。这意味着不能在 store 中使用私有属性
  2. Setup store 比 Option Store 带来了更多的灵活性,因为可以在一个 store 内创建侦听器,并自由地使用任何组合式函数。
  3. Setup store 可以依赖于全局提供的属性,比如路由。任何应用层面提供的属性都可以在 store 中使用 inject() 访问,就像在组件中一样。
// Option 对象 
defineStore('myPinia', {
  state: () => { return { } },
  getters: { },
  actions: { }
});

// Setup 函数
defineStore('myPinia', () => {
  const count = ref(0)
  const doubleCount = computed(() => { })
  function increment() { }
  
  return { count, doubleCount, increment }
})

解构 Store

storeToRefs 解构 store 中的 状态getter,跳过所有的 action 或非响应式 (不是 ref 或 reactive) 的属性。‌
action非响应式 (不是 ref 或 reactive) 的属性 直接在 Store 中解构。

// 定义
import { defineStore } from 'pinia'
import { computed, ref } from 'vue';
export const useMyPiniaStore = defineStore('myPinia', () => {
  const count = ref(1);
  const doubleCount = computed(() => count.value * 2)
  const increment = () => { count.value++ }
  const unRective = '非响应式数据'

  return { count, doubleCount, increment, unRective }
});

// 解构
<script setup>
  import { useMyPiniaStore } from '../store/store'
  import { storeToRefs } from 'pinia'
  const store = useMyPiniaStore()
  const { count, doubleCount } = storeToRefs(store)
  const { increment, unRective } = store
</script>

State

// 方式1 -- 不设置状态类型
export const useMyPiniaStore = defineStore('myPinia', {
  state: () => { return { count: 0 } }
});

// 方式2 -- ts 严格模式 定义接口规定数据的类型
interface UserInfo {
  name: string
  age: number,
  count: number
}
defineStore('myPinia', {
  state: () => {
    return {
      userList: [] as UserInfo[],
      user: null as UserInfo | null,
      count: 0 as UserInfo['count']
    }
  }
})

访问 state

// 注意,新的属性如果没有在 state() 中被定义,则不能被添加。它必须包含初始状态。
// 例如:如果 secondCount 没有在 state() 中定义,我们无法执行 store.secondCount = 2。
const store = useMyPiniaStore()
store.count++

重置 state

// 选项式API(options API)
const store = useMyPiniaStore()
store.$reset()

// 组合式API(Composition API)
defineStore('myPinia', () => {
  const count = ref(0)
  const $reset = () => { count.value = 0 }
  
  return { count, $reset }
})

选项式 API 中的 mapState 和 mapWritableState

// mapState
import { mapState } from 'pinia'
import { useMyPiniaStore } from '../store/store'
export default {
  computed: {
    ...mapState(useMyPiniaStore, ['count'])  // this.count => store.count
    ...mapState(useMyPiniaStore, {
      myOwnName: 'count', // this.myOwnName => store.count
      double: store => store.count * 2 // 定义一个函数获取 store.count
    })
  }
}

// mapWritableState -- 不能像 mapState() 那样传递一个函数
export default {
  computed: {
    ...mapWritableState(useMyPiniaStore, ['count'])  // this.count => store.count
    ...mapWritableState(useMyPiniaStore, { myOwnName: 'count' }) // this.myOwnName => store.count
  }
}

对于像数组这样的集合,并不一定需要使用 mapWritableState(),mapState() 也允许你调用集合上的方法,除非想用 cartItems = [] 替换整个数组。

变更 state

// 1. patch 方法。允许用一个 state 的补丁对象在同一时间更改多个属性
store.$patch({
  count: store.count + 1,
  age: 120,
  name: 'DIO'
})

// 2. 有些变更很难实现或者很耗时:任何集合的修改(例如,向数组中添加、移除一个元素或是做 splice 操作)
//都需要创建一个新的集合。因此,$patch 方法也接受一个函数来组合这种难以用补丁对象实现的变更。
store.$patch((state) => {
  state.items.push({ name: 'shoes', quantity: 1 })
  state.hasChanged = true
})

替换 state

// 不能完全替换掉 store 的 state,因为那样会破坏其响应性。但是可以 patch 它。
store.$patch({ count: 66 })
// 也可以通过变更 pinia 实例的 state 来设置整个应用的初始 state。
pinia.state.value = {}

订阅 state

类似于 Vuex 的 subscribe 方法,你可以通过 store 的 $subscribe() 方法侦听 state 及其变化。比起普通的 watch(),使用 $subscribe() 的好处是 subscriptions 在 patch 后只触发一次。

cartStore.$subscribe((mutation, state) => {
  // import { MutationType } from 'pinia'
  mutation.type // 'direct' | 'patch object' | 'patch function'
  // 和 cartStore.$id 一样
  mutation.storeId // 'cart'
  // 只有 mutation.type === 'patch object'的情况下才可用
  mutation.payload // 传递给 cartStore.$patch() 的补丁对象。

  // 每当状态发生变化时,将整个 state 持久化到本地存储。
  localStorage.setItem('cart', JSON.stringify(state))
})

默认情况下,state subscription 会被绑定到添加它们的组件上 (如果 store 在组件的 setup() 里面)。这意味着,当该组件被卸载时,它们将被自动删除。如果想在组件卸载后依旧保留它们,请将 { detached: true } 作为第二个参数,以将 state subscription 从当前组件中分离。

<script setup>
  const someStore = useSomeStore()
  // 此订阅器即便在组件卸载之后仍会被保留
  someStore.$subscribe(callback, { detached: true })
</script>

可以在 pinia 实例上使用 watch() 函数侦听整个 state。

watch(
  pinia.state,
  (state) => {
    // 每当状态发生变化时,将整个 state 持久化到本地存储。
    localStorage.setItem('piniaState', JSON.stringify(state))
  },
  { deep: true }
)

Getter

Getter 完全等同于 store 的 state 的计算值。

// option对象 -- 定义getter
export const useMyPiniaStore = defineStore('myPinia', {
  state: () => ({
    count: 0
  }),
  getters: {
    //方式1 -- 基本使用
    doubleCount: (state) => state.count * 2,
    //方式2 -- 使用其他 getter,在 TypeScript 中必须定义返回类型。
     doublePlusOne(): number {
      return this.doubleCount + 1
    },
    //方式3 -- 返回一个函数
    countAddNum: (state) => {
      return (num: number) => state.count + num
    },
    //方法4 -- 访问其他 store 的getter
    otherGetter: (state) => {
      const otherStore = useOtherStore()
      return state.count + otherStore.count
    }
  }
})

在 setup() 中使用 getter

<script setup>
  import { useMyPiniaStore } from '../store/store'
  const store = useMyPiniaStore()
  store.count = 3
  store.doubleCount // 6
</script>

使用 mapState 辅助函数映射 getter

import { mapState } from 'pinia'
import { useMyPiniaStore } from '../store/store'
export default {
  computed: {
    ...mapState(useMyPiniaStore, ['doubleCount']), // this.doubleCount => store.doubleCount
    ...mapState(useMyPiniaStore, {
      myOwnName: 'doubleCount', // this.myOwnName => store.doubleCount
      double: (store) => store.doubleCount // double => store.doubleCount
    })
  }
}

Action

定义和使用 action

action 相当于组件中的 method。
action 可以是异步的。

// 定义 actions
export const useMyPiniaStore = defineStore('myPinia', {
  state: () => { return { count: 6 } },
  actions: {
    increment() { this.count++ },
    randomizeCounter() { this.count = Math.round(100 * Math.random()) },
    // 异步
    asyncCount() { setTimeout(() => this.count++, 1000) },
    // 接收参数
    countAddNumber(num: number) { this.count += num }
  }
});
// 使用 action
<template>
  <button @click="store.increment()">increment</button>
  <button @click="store.randomizeCounter()">randomizeCounter</button>
  <button @click="store.asyncCount()">asyncCount</button>
  <button @click="store.countAddNumber(100)">countAddNumber</button>
  <p>{{ store.count }}</p>
</template>

<script setup>
  import { useMyPiniaStore } from './store/store'
  const store = useMyPiniaStore()
</script>

mapActions

import { mapActions } from 'pinia'
import { useMyPiniaStore } from '../store/store'
export default {
  methods: {
    ...mapActions(useMyPiniaStore, ['increment']) // this.increment() => store.increment()
    ...mapActions(useMyPiniaStore, { myOwnName: 'increment' }) // this.myOwnName() => store.increment()
  },
}

订阅 action

通过 store.$onAction() 来监听 action 和它们的结果。传递给它的回调函数会在 action 本身之前执行。after 表示在 promise 解决之后,允许你在 action 解决后执行一个回调函数。onError 允许你在 action 抛出错误 / reject 时执行一个回调函数。

const unsubscribe = useMyPiniaStore.$onAction(
  ({
    name, // action 名称
    store, // store 实例,类似 `useMyPiniaStore`
    args, // 传递给 action 的参数数组
    after, // 在 action 返回或解决后的钩子
    one rror // action 抛出或拒绝的钩子
  }) => {
    const startTime = Date.now() // 为这个特定的 action 调用提供一个共享变量
    console.log(`Start "${name}" with params [${args.join(', ')}].`) // 这将在执行 "store "的 action 之前触发。
    after((result) => { // 这将在 action 成功并完全运行后触发。它等待着任何返回的 promise
      console.log(`Finished "${name}" after ${Date.now() - startTime}ms.\nResult: ${result}.`)
    })
    one rror((error) => { // 如果 action 抛出或返回一个拒绝的 promise,这将触发
      console.warn(`Failed "${name}" after ${Date.now() - startTime}ms.\nError: ${error}.`)
    })
  }
)

// 手动删除监听器
unsubscribe()

默认情况下,action 订阅器会被绑定到添加它们的组件上(如果 store 在组件的 setup() 内)。当该组件被卸载时,它们将被自动删除。如果想在组件卸载后依旧保留它们,请将 true 作为第二个参数传递给 action 订阅器,将其从当前组件中分离。

<script setup>
  const someStore = useSomeStore()
  someStore.$onAction(callback, true) // 此订阅器即便在组件卸载之后仍会被保留
</script>

总结

Pinia的好处主要体现在简单易用、‌TypeScript支持、‌分治管理、‌性能优化等方面。‌
简单易用:‌Pinia的API设计简洁明了,‌易于理解和使用。‌与Vuex相比,‌它不需要定义复杂的store和module,‌而是采用类似于Vue组件的方式来处理状态管理,‌使得状态管理更加直观和易于上手。‌
TypeScript支持:‌Pinia内置了对TypeScript的支持,‌能够提供类型安全的状态管理,‌有效避免了类型错误的问题。‌这对于使用TypeScript开发的Vue项目来说,‌是一个重要的优势。‌
分治管理:‌Pinia支持将状态分割成多个模块,‌每个模块可以独立管理自己的状态。‌这种分治的设计使得应用状态更加清晰可控,‌有助于提高代码的可维护性和可读性。‌
性能优化:‌Pinia内部使用了响应式编程的技术,‌提高了状态管理的性能和响应速度。‌此外,‌它还支持使用插件来进行状态的持久化和调试,‌进一步提高了开发效率。‌
综上所述,‌Pinia作为一个基于Vue 3的状态管理库,‌通过其简洁的API设计、‌内置的TypeScript支持、‌模块化的状态管理以及性能优化等特点,‌为Vue项目的开发提供了高效且便捷的状态管理解决方案。‌

标签:count,useMyPiniaStore,Vue,const,Pinia,state,State,pinia,store
From: https://blog.csdn.net/weixin_54092687/article/details/140520675

相关文章

  • 学习vue第一天
    文章目录1.什么是Vue?2.渐进式框架3.如何新建一个vue项目1.什么是Vue?Vue(发音为/vjuː/,类似 view)是一款用于构建用户界面的JavaScript框架。它基于标准HTML、CSS和JavaScript构建,并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。无......
  • 修改el-popover样式不生效问题,vue中element-ui样式修改不生效问题
    修改el-popover样式不生效问题在最近公司写的项目中,使用到了el-popover,但是想要修改弹出层中文本的字体样式,尝试过很多方法之后,发现修改的样式都没有生效,查阅资料发现:el-popover比较特殊,他生成的div不在当前组件之内,甚至不在App.vue组件的div内,他和App.vue组件的div平级,需要设置......
  • vue的组件嵌套关系
    组件嵌套关系利用组件结构完成嵌套建立以下vue将Header.vueMain.vueAside.vue引入App.vue中Article.vue引入Main.vue,Item.vue引入Aside.vue中Header.vue代码:Header</template><script></script><stylescoped>.h......
  • 自动导入unplugin-auto-import+unplugin-vue-components
    文章介绍接下来将会以Vite+Vue3+TS的项目来举例实现在我们进行项目开发时,无论是声明响应式数据使用的ref、reactive,或是各种生命周期,又或是computed、watch、watchEffect、provide-inject。这些都需要前置引入才能使用:import{ref,reactive,onMounted,watch,provid......
  • vue的侦听器/表单输入绑定和模板引用
    1.侦听器侦听器在修改数据过程中,实时的侦听数据,将修改前数据和修改后数据记录2.表单输入绑定在input标签中输入v-model指令可以实时的显示input标签中输入的内容,v-model.lazy指令为不实时显示,在input标签中输入的内容用鼠标点击空白页面或ENTER后显示3.模板引用直接读取DOM......
  • vue的数组变化侦测/计算属性/Class绑定/Style绑定
    1.数组变化侦测效果图单击push按钮后可增加其内容,直接显示在页面中单击concat按钮后修改数组,不直接显示在页面中,将数组赋值后显示addArrayt(){//不引起ul自动更新this.names.concat(["sakura"])//若不赋值则无法显示this.names=this.names.concat(["sakura"])}2.计......
  • 【计算机毕业设计】ssm499智能社区管理系统的设计与实现+vue
    现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本智能社区管理系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息,使用这种软件工具可以帮助管理人员提高事务处理效率,达......
  • 【vue前端项目实战案例】Vue仿京东商城App
    本文将介绍一款仿“京东商城”商品信息展示的电商类App。该案例是基于Vue2.0+VueRouter+webpack+ES6等技术栈实现的一款App,很适合初学者进行学习。项目源码在文章末尾1项目概述项目是一款仿“京东商城”的商品信息展示的App,主要实现了以下功能。商城首页轮......
  • 计算机项目/基于Spring Boot+Vue的生活用品购物平台设计与实现/毕设/网站健身
    基于SpringBoot+Vue的生活用品购物平台设计与实现摘要:生活用品购物平台系统采用SpringBoot作为后端框架,提供高效的数据处理和业务逻辑实现;前端使用Vue.js,通过其响应式数据绑定和组件化开发,使得用户界面更具交互性和可扩展性,选择MySQL数据库存储数据。系统分为用户模块和管......
  • 【计算机毕业设计】ssm485科研经费管理系统研究与开发+vue
    身处网络时代,随着网络系统体系发展的不断成熟和完善,人们的生活也随之发生了很大的变化,人们在追求较高物质生活的同时,也在想着如何使自身的精神内涵得到提升,而读书就是人们获得精神享受非常重要的途径。为了满足人们随时随地只要有网络就可以看书的要求,科研经费管理系统被开......