关于小菠萝的搬运

252 阅读5分钟

文章目录

1、Pinia 介绍

① 核心特性

② 核心概念

Ⅰ. state Ⅱ. getters Ⅲ. actions Ⅳ. 提示:Pinia 中没有 mutations。

③ 基本示例

④ Pinia vs Vuex

1、Pinia 介绍

一个全新的用于 Vue 的状态管理库。下一个版本的 Vuex,也就是 Vuex 5.0。

image.png

Pinia最初是一个实验,目的是在2019年11月左右重新设计 Vue 状态管理在Composite API上的样子,也就是下一代 Vuex:

之前的Vuex主要服务于Vue2,Options API

如果想要在Vue3中使用Vuex,需要使用 4.x 版本:只是一个过渡的选择,还有很大的缺陷

所以在Vue3伴随着Composition API诞生之后,也设计了全新的Vuex:Pinia,也就是Vuex5

① 核心特性

Vue2和Vue3都支持

除了初始化安装和SSR配置之外,两者的API都是相同的:官方文档中主要针对Vue3进行说明,必要的时候会提供Vue2的注释

支持VueDevTools

跟踪actions、mutations的时间线:在使用容器的组件中就可以观察到容器本身

支持time travel (时间旅行) 更容易的调试功能:在Vue2中Pinia使用Vuex的现有接口,所以不能与Vuex一起使用 但是针对Vue3中的调试工具支持还不够完美,比如还没有time-travel调试功能

模块热更新

无需重新加载页面即可修改您的容器:热更新的时候保持任何现有状态

支持使用插件扩展Pinia功能

相比Vuex有更好完美的TypeScript支持

支持服务端渲染

② 核心概念

Pinia从使用角度和之前的Vuex几乎是一样的,比Vuex更简单了。

在Vuex中有四个核心概念:State、Getters、Mutations、Actions

在Pinia中:State、Getters、Actions (同步异步都支持)

Store(如 Pinia)是一个保存状态和业务逻辑的实体,它不绑定到您的组件树。换句话说,它承载全局 state。它有点像一个始终存在的组件,每个人都可以读取和写入。它有三个核心概念。

Ⅰ. state

类似组件的 data ,用来存储全局状态

{
  todos: [
    { id: 1, title: '吃饭', done: false },
    { id: 1, title: '吃饭', done: true },
    { id: 1, title: '吃饭', done: false }
  ]
}
Ⅱ. getters

类似组件的 computed,根据已有 State 封装派生数据,也具有缓存的特性

doneCount () {
  return todos.filter(item => item.done).length
}
Ⅲ. actions

类似组件的 methods,用来封装业务逻辑,同步异步都可以;在 Vuex 中同步操作用 mutations,异步操作用 actions,太麻烦!!!

Ⅳ. 提示:Pinia 中没有 mutations。

③ 基本示例

这就是在 API 方面使用 pinia 的样子(请务必查看入门以获取完整说明)。您首先创建一个 store:

// stores/counter.js
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => {
    return { count: 0 }
  },
  // could also be defined as
  // state: () => ({ count: 0 })
  actions: {
    increment() {
      // 在 Vuex 中我们需要搞两步:1. 定义 mutations 2. 提交 mutations
      this.count++
    },
  },
})

然后在组件中使用它:

import { useCounterStore } from '@/stores/counter'

export default {
  setup() {
    const counter = useCounterStore()

    counter.count++
    // with autocompletion ✨
    counter.$patch({ count: counter.count + 1 })
    // or using an action instead
    counter.increment()
  },
}

您甚至可以使用函数(类似于组件setup())为更高级的用例定义 Store:

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  function increment() {
    count.value++
  }

  return { count, increment }
})

如果你仍然不熟悉setup()Composition API,别担心,Pania 也支持类似Vuex 的一组地图助手。您以相同的方式定义商店,但随后使用mapStores(), mapState(),或mapActions():

const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  getters: {
    double: (state) => state.count * 2,
  },
  actions: {
    increment() {
      this.count++
    }
  }
})
const useUserStore = defineStore('user', {
  // ...
})
export default {
  computed: {
    // other computed properties
    // ...
    // gives access to this.counterStore and this.userStore
    ...mapStores(useCounterStore, useUserStore)
    // gives read access to this.count and this.double
    ...mapState(useCounterStore, ['count', 'double']),
  },
  methods: {
    // gives access to this.increment()
    ...mapActions(useCounterStore, ['increment']),
  },
}

您将在核心概念中找到有关每个地图助手的更多信息。

④ Pinia vs Vuex

Pinia试图尽可能接近Vuex的理念。它旨在测试Vuex下一次迭代的提案,并且取得了成功,因为我们目前有一个针对Vuex5的开放式 RFC,其API与Pinia使用的 API 非常相似。请注意,Pinia的作者I (Eduardo) 是Vue.js核心团队的一员,并积极参与Router和Vuex等API的设计。我个人对这个项目的意图是重新设计使用全局Store的体验,同时保持 Vue 平易近人的哲学。我让Pania的API与Vuex一样接近,因为它不断向前发展,使人们可以轻松地迁移到Vuex,甚至在未来融合这两个项目(在Vuex下)。

关于版本问题:

Vuex 当前最新版本是 4.x

Vuex 4 用于 Vue 3

Vuex 3 用于 Vue 2

Pinia 当前最新版本是 2.x

既支持 Vue 2 也支持 Vue 3

Pinia 可以认为就是 Vuex 5,因为它的作者是官方的开发人员,并且已经被官方接管了。

有两种可能(个人猜测): 把 Pinia 合并到 Vuex 中作为 Vuex 5.x

两个仓库独立发展,官方会推荐使用 Pinia

我给大家的建议就是:可以直接去使用 Pinia。

Pinia API 与 Vuex ≤4 有很大不同,即:

没有 mutations 。mutations 被认为是非常冗长的。最初带来了 devtools 集成,但这不再是问题。 不再有模块的嵌套结构。 您仍然可以通过在另一个 store 中导入和使用 store 来隐式嵌套 store,但 Pinia 通过设计提供扁平结构,同时仍然支持 store 之间的交叉组合方式。您甚至可以拥有 store 的循环依赖关系。

更好typescript支持。 无需创建自定义的复杂包装器来支持 TypeScript,所有内容都是类型化的,并且 API 的设计方式尽可能地利用 TS 类型推断。

不再需要注入、导入函数、调用它们,享受自动补全!

无需动态添加 stores,默认情况下它们都是动态的,您甚至不会注意到。 请注意,您仍然可以随时手动使用 store 来注册它,但因为它是自动的,所以您无需担心。

没有命名空间模块。 鉴于 store 的扁平架构,“命名空间” store 是其定义方式所固有的,您可以说所有 stores 都是命名空间的。

Pinia 就是更好的 Vuex,建议在你的项目中可以直接使用它了,尤其是使用了TypeScript 的项目。

开始使用啦~

① 项目搭建

使用vite初始化项目:npm create vite@latest

创建项目名:vue3-ts-pinia

选择项目框架:vue

选择项目语言:vue-ts

进入到创建的项目目录下,安装依赖:npm install

启动项目:npm run dev

② 安装 使用yarn安装:yarn add pinia

使用npm安装:npm install pinia

目前安装的pinia版本是2.0.12;

提示:

如果您的应用程序使用 Vue 2,您还需要安装组合式 api 包: @vue/composition-api。 如果你使用的是 Vue CLI,你可以试试这个非官方插件。

③ 初始化配置 在项目的src/main.ts文件中进行配置:

import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia' // 导入 pinia

// 创建 pinia 实例对象
const pinia = createPinia()

createApp(App)
  .use(pinia) // 挂载 pinia 到 vue 实例对象上
  .mount('#app')

在src目录下创建一个store文件夹,再在这个文件夹中创建一个index.ts文件,用来编写pinia代码;

2、定义和使用 Store

① 定义Store

在src/store/index.ts文件中,创建pinia容器:


import { defineStore } from 'pinia' // 导入 pinia 中的功能模块

// 创建容器
// 参数一:容器名
// 参数二:容器的内容
const useMainStore =  defineStore('main', {
  // pinia 状态管理的数据,通过箭头函数返回一个对象
  // 相当于 vue 中的 data 数据
  state: () => {
    return {
      message1: 'Hello',
      message2: 'Pinia',
      count: 1
    }
  },

  // 相当于 vue 中的 computed 计算属性
  getters: {},

  // 相当于 vue 中的 methods 方法
  actions: {}
})

// 导出容器
export {
  useMainStore
}

注意:

需要导入defineStore方法来创建pinia来创建容器; defineStore方法有两个参数: 参数一:是一个字符串,自定义的容器名 (唯一的); 参数二:是一个对象,pinia的配置都在这里面; 给容器命名的规范一般是:use+自定义的容器名+Store; 最后将创建的容器进行导出;

② 使用 Store

在src/components目录下创建一个新的vue文件:MyPinia.vue,在这个组件中体验下pinia的使用:

<template>
  <h2>{{ mainStore.message1 }}</h2>
  <h3>{{ mainStore.message2 }}</h3>

  <button @click="getStore">操作 count</button>
</template>

<script setup lang="ts">
// 导入 pinia 实例
import { useMainStore } from '../store/index'

// 实例化容器
const mainStore = useMainStore()

// 按钮点击事件
const getStore = () => {
  console.log(mainStore.message1 + " " + mainStore.message2);
}
</script>

<style scoped></style>

注意: Store 是一个 reactive 包裹的对象,所有访问其中的成员不需要 .value。

在src/App.vue主页面中导入上述组件,在页面中进行显示:

<template>
  <MyPinia />
</template>

<script setup lang="ts">
import MyPinia from './components/MyPinia.vue'
</script>

<style></style>

③ 解构 state 数据 体验一下通过解构获取到的state,页面一样会渲染出数据:

<template>
  <h2>{{ message1 }}</h2>
  <h3>{{ message2 }}</h3>

  <button @click="getStore">打印 message</button>
</template>
<script setup lang="ts">
// 导入 pinia 实例
import { useMainStore } from '../store/index'

// 实例化容器
const mainStore = useMainStore()

// 体验通过解构获取 pinia 的 state
const { message1, message2, count } = mainStore

// 按钮点击事件
const getStore = () => {
  console.log(message1 + " " + message2 + " " + count);
}
</script>

<style scoped></style>

来体验一下通过解构出来的state值和不通过解构得到的state值有什么区别:

<template>
  <h2>{{ message1 }}</h2>
  <h3>{{ message2 }}</h3>
  <h4>没有通过解构获得的 count:{{ mainStore.count }}</h4>
  <h4>通过解构获得的 count:{{ count }}</h4>

  <button @click="getStore">按钮</button>
</template>
<script setup lang="ts">
// 导入 pinia 实例
import { useMainStore } from '../store/index'

// 实例化容器
const mainStore = useMainStore()

// 体验通过解构获取 pinia 的 state,解构出来的值不是响应式的
const { message1, message2, count } = mainStore

// 按钮点击事件
const getStore = () => {
  mainStore.count++
}
</script>
<style scoped></style>

从上图可以很清楚的看到,通过解构获取到state中的数据不是响应式的

如果想要解构拿到 Store 中的响应式数据可以使用 storeToRefs。


<template>
  <h2>{{ message1 }}</h2>
  <h3>{{ message2 }}</h3>
  <h4>没有通过解构获得的 count:{{ mainStore.count }}</h4>
  <h4>通过解构获得的 count:{{ count }}</h4>

  <button @click="getStore">按钮</button>
</template>
<script setup lang="ts">
// 导入 pinia 中的 storeToRefs 方法
import { storeToRefs } from 'pinia';
// 导入 pinia 实例
import { useMainStore } from '../store/index'

// 实例化容器
const mainStore = useMainStore()

// 体验通过解构获取 pinia 的 state,解构出来的值不是响应式的
// 将 pinia 实例对象通过 storeToRefs 包裹起来的时候,里面的值就是响应式的了
const { message1, message2, count } = storeToRefs(mainStore)

// 按钮点击事件
const getStore = () => {
  mainStore.count++
}
</script>

<style scoped></style>

可以从上图看出,通过 storeToRefs 方法包裹pinia实例,再通过解构获取的state就是响应式的了。

接着是actions 了

1、状态操作 actions

actions 可以通过完全输入(和自动完成 ✨)支持访问整个容器实例。actions 是异步的,可以在其中等待任何 API 调用甚至其他 actions 。

① 状态更新 首先在src/store/index.ts文件中的pinia实例中,创建一个actions方法:

import { defineStore } from 'pinia' // 导入 pinia 中的功能模块

// 创建容器
// 参数一:容器名
// 参数二:容器的内容
const useMainStore =  defineStore('main', {
  // pinia 状态管理的数据,通过箭头函数返回一个对象
  // 相当于 vue 中的 data 数据
  state: () => {
    return {
      message1: 'Hello',
      message2: 'Pinia',
      count: 1
    }
  },

  // 相当于 vue 中的 computed 计算属性
  getters: {},

  // 相当于 vue 中的 methods 方法
  actions: {
    **// 1、count 加法方法
    addCount() {
      this.count++
    }**
  }
})

// 导出容器
export {
  useMainStore
}

在src/components/MyPinia.vue页面组件中进行pinia的actions方法的调用,此处有三种调用的方式:

方法一: 在标签中绑定actions事件
<template>
  <h4>没有通过解构获得的 count:{{ mainStore.count }}</h4>
  <h4>通过解构获得的 count:{{ count }}</h4>

  <button @click="getStore">按钮</button>
  **<button @click="mainStore.addCount">模板调用 actions 的 addCount 方法</button>**
</template>
<script setup lang="ts">
// 导入 pinia 中的 storeToRefs 方法
import { storeToRefs } from 'pinia';
// 导入 pinia 实例
import { useMainStore } from '../store/index'

// 实例化容器
const mainStore = useMainStore()

// 体验通过解构获取 pinia 的 state,解构出来的值不是响应式的
// 将 pinia 实例对象通过 storeToRefs 包裹起来的时候,里面的值就是响应式的了
const { message1, message2, count } = storeToRefs(mainStore)

// 按钮点击事件
const getStore = () => {
  mainStore.count++
}
</script>

<style scoped></style>
方法二: 自定义一个方法,在这个方法中去调用对应的actions方法:
<template>
  <h2>{{ message1 }}</h2>
  <h3>{{ message2 }}</h3>
  <h4>没有通过解构获得的 count:{{ mainStore.count }}</h4>
  <h4>通过解构获得的 count:{{ count }}</h4>

  <button @click="getStore">按钮</button>
  <button @click="mainStore.addCount">模板调用 actions 的 addCount 方法</button>
  **<button @click="addCount">自定义方法调用 actions 的 addCount 方法</button>**
</template>
<script setup lang="ts">
// 导入 pinia 中的 storeToRefs 方法
import { storeToRefs } from 'pinia';
// 导入 pinia 实例
import { useMainStore } from '../store/index'

// 实例化容器
const mainStore = useMainStore()

// 体验通过解构获取 pinia 的 state,解构出来的值不是响应式的
// 将 pinia 实例对象通过 storeToRefs 包裹起来的时候,里面的值就是响应式的了
const { message1, message2, count } = storeToRefs(mainStore)

// 按钮点击事件
const getStore = () => {
  console.log(message1 + " " + message2 + " " + count);
  mainStore.count++
}

**// 自定义的 count 加法方法
const addCount = () => {
  mainStore.addCount()
}**
</script>

<style scoped></style>
方法三: pinia中提供了一个方法$patch

向上面两种方法调用actions去改变state的时候,会有留下state值的历史记录(时间旅行)。但是如果不通过actions,而是直接去修改state的值的话,例如直接通过mainStore.count++这种方式修改state的值,就不会留下state的历史记录。而$patch方法可以直接去修改state中的值,并且留下历史记录:

<template>
  <h2>{{ message1 }}</h2>
  <h3>{{ message2 }}</h3>
  <h4>没有通过解构获得的 count:{{ mainStore.count }}</h4>
  <h4>通过解构获得的 count:{{ count }}</h4>

  <button @click="getStore">按钮</button>
  <button @click="mainStore.addCount">模板调用 actions 的 addCount 方法</button>
  <button @click="addCount">自定义方法调用 actions 的 addCount 方法</button>
</template>

<script setup lang="ts">
// 导入 pinia 中的 storeToRefs 方法
import { storeToRefs } from 'pinia';
// 导入 pinia 实例
import { useMainStore } from '../store/index'

// 实例化容器
const mainStore = useMainStore()

// 体验通过解构获取 pinia 的 state,解构出来的值不是响应式的
// 将 pinia 实例对象通过 storeToRefs 包裹起来的时候,里面的值就是响应式的了
const { message1, message2, count } = storeToRefs(mainStore)

// 按钮点击事件
const getStore = () => {
  console.log(message1 + " " + message2 + " " + count);
  mainStore.count++
}

// 自定义的 count 加法方法
const addCount = () => {
  **// $patch 方法,可以直接修改 state 中的值
  mainStore.$patch({
    message1: '你好',
    message2: '菠萝',
    count: 66,
  })**
}
</script>

<style scoped></style>

使用方法三的语法应用某些更改确实很困难或成本很高: 任何集合修改(例如,从数组中推送、删除、拼接元素)都需要您创建一个新集合。正因为如此,该 $patch 方法还接受一个函数来对这种难以用补丁对象应用的改变进行分组:

<template>
  <h2>{{ message1 }}</h2>
  <h3>{{ message2 }}</h3>
  <h4>没有通过解构获得的 count:{{ mainStore.count }}</h4>
  <h4>通过解构获得的 count:{{ count }}</h4>

  <button @click="getStore">按钮</button>
  <button @click="mainStore.addCount">模板调用 actions 的 addCount 方法</button>
  <button @click="addCount">自定义方法调用 actions 的 addCount 方法</button>
</template>

<script setup lang="ts">
// 导入 pinia 中的 storeToRefs 方法
import { storeToRefs } from 'pinia';
// 导入 pinia 实例
import { useMainStore } from '../store/index'

// 实例化容器
const mainStore = useMainStore()

// 体验通过解构获取 pinia 的 state,解构出来的值不是响应式的
// 将 pinia 实例对象通过 storeToRefs 包裹起来的时候,里面的值就是响应式的了
const { message1, message2, count } = storeToRefs(mainStore)

// 按钮点击事件
const getStore = () => {
  console.log(message1 + " " + message2 + " " + count);
  mainStore.count++
}

// 自定义的 count 加法方法
const addCount = () => {
  // mainStore.addCount()

  // 1、$patch 方法,可以直接修改 state 中的值
  // mainStore.$patch({
  //   message1: '你好',
  //   message2: '菠萝',
  //   count: 66,
  // })

  **// 2、$patch 方法,通过分组改变 state 中的值
  mainStore.$patch(state => {
    state.message1 += ' Hello'
    state.message2 += ' Pinia'
    state.count++
  })**
}
</script>

<style scoped></style>

您也可以完全自由地设置您想要的任何参数并返回任何内容。调用 actions 时,一切都会自动推断!

② 重置状态 $reset

上面我说过,pinia的state更新的时候会有历史记录,即时间旅行,如果想要将数据重置到最开始更新数据的时候,pinia提供了一个方法:$reset:

<template>
  <h2>{{ message1 }}</h2>
  <h3>{{ message2 }}</h3>
  <h4>没有通过解构获得的 count:{{ mainStore.count }}</h4>
  <h4>通过解构获得的 count:{{ count }}</h4>

  <button @click="getStore">按钮</button>
  <button @click="mainStore.addCount">模板调用 actions 的 addCount 方法</button>
  <button @click="addCount">自定义方法调用 actions 的 addCount 方法</button>
  **<button @click="resetCount">重置 state**</**button>**
</template>
<script setup lang="ts">
// 导入 pinia 中的 storeToRefs 方法
import { storeToRefs } from 'pinia';
// 导入 pinia 实例
import { useMainStore } from '../store/index'

// 实例化容器
const mainStore = useMainStore()

// 体验通过解构获取 pinia 的 state,解构出来的值不是响应式的
// 将 pinia 实例对象通过 storeToRefs 包裹起来的时候,里面的值就是响应式的了
const { message1, message2, count } = storeToRefs(mainStore)

// 按钮点击事件
const getStore = () => {
  console.log(message1 + " " + message2 + " " + count);
  mainStore.count++
}

// 自定义的 count 加法方法
const addCount = () => {
  // mainStore.addCount()

  // 1、$patch 方法,可以直接修改 state 中的值
  // mainStore.$patch({
  //   message1: '你好',
  //   message2: '菠萝',
  //   count: 66,
  // })

  // 2、$patch 方法,通过分组改变 state 中的值
  mainStore.$patch(state => {
    state.message1 += ' Hello'
    state.message2 += ' Pinia'
    state.count++
  })
}

**// 重置 count 数据的方法
const resetCount = () => {
  mainStore.$reset()
}**
</script>

<style scoped></style>

③ 跨容器调用

在pinia中没有modules概念,可以在pinia中同时创建多个容器实例;可以在每一个容器中分别调用其他容器中的值,进行一些列的操作;

首先在src/store/index.ts文件中创建一个新的容器,在新的容器中可以在actions中,获取到其他容器实例,调用其中的state值:

import { defineStore } from 'pinia' // 导入 pinia 中的功能模块

// 创建容器
// 参数一:容器名
// 参数二:容器的内容
const useMainStore =  defineStore('main', {
  // pinia 状态管理的数据,通过箭头函数返回一个对象
  // 相当于 vue 中的 data 数据
  state: () => {
    return {
      message1: 'Hello',
      message2: 'Pinia',
      count: 1,
      number: 666
    }
  },

  // 相当于 vue 中的 computed 计算属性
  getters: {},

  // 相当于 vue 中的 methods 方法
  actions: {
    // 1、count 加法方法
    addCount() {
      this.count++
    }
  }
})

const useProjectStore =  defineStore('project', {
  // pinia 状态管理的数据,通过箭头函数返回一个对象
  // 相当于 vue 中的 data 数据
  state: () => {
    return {
      username: 'liangshuang',
      age: 21
    }
  },

  // 相当于 vue 中的 computed 计算属性
  getters: {},

  // 相当于 vue 中的 methods 方法
  actions: {
    // 1、age 加法方法
    addAge() {
      // 实例化 pinia 容器
      const mainState = useMainStore()
      this.age += this.age + mainState.count
    }
  }
})

// 导出容器
export {
  useMainStore,
  useProjectStore
}

在组件中可以有两种方法去调用pinia中跨容器调用的方法:

方法一: 直接在标签中去调用容器中的方法

<template>
  <h4>{{ age }}</h4>
  **<button @click="projectStore.addAge">模板跨容器调用</button>**
</template>

<script setup lang="ts">
// 导入 pinia 中的 storeToRefs 方法
import { storeToRefs } from 'pinia';
// 导入 pinia 实例
import { useMainStore, useProjectStore } from '../store/index'

// 实例化容器
const mainStore = useMainStore()
const projectStore = useProjectStore()

// 体验通过解构获取 pinia 的 state,解构出来的值不是响应式的
// 将 pinia 实例对象通过 storeToRefs 包裹起来的时候,里面的值就是响应式的了
const { message1, message2, count, number } = storeToRefs(mainStore)
const { username, **age** } = storeToRefs(projectStore)
</script>

<style scoped></style>

方法二: 自定义一个调用容器方法的函数

<template>
  <h4>{{ age }}</h4>
  <button @click="projectStore.addAge">模板跨容器调用</button>
  **<button @click="addAge">自定义方法跨容器调用</button>**
</template>

<script setup lang="ts">
// 导入 pinia 中的 storeToRefs 方法
import { storeToRefs } from 'pinia';
// 导入 pinia 实例
import { useMainStore, useProjectStore } from '../store/index'

// 实例化容器
const mainStore = useMainStore()
const projectStore = useProjectStore()

// 体验通过解构获取 pinia 的 state,解构出来的值不是响应式的
// 将 pinia 实例对象通过 storeToRefs 包裹起来的时候,里面的值就是响应式的了
const { message1, message2, count, number } = storeToRefs(mainStore)
const { username, age } = storeToRefs(projectStore)

**// 自定义的 age 加法方法
const addAge = () => {
  projectStore.age += mainStore.number
}**
</script>

<style scoped></style>

④ actions 的操作

const changeStore = () => {
  // 方式四:逻辑比较多的时候可以封装到 actions 做处理
  // 封装好 actions 之后,直接调用
  mainStore.adds(10)
}

封装 actions:

// 类似与组件的 methods ,封装业务逻辑,修改 state 
actions: {
  // 注意:不能使用箭头函数定义action,因为箭头函数绑定外部this
  adds(num: number) {      
    this.count = this.count + num
    this.foo++
    // 同样建议使用 $patch() 
    // this.$patch({})
    // this.Spatch(state = {……})
  }
}

⑤ 注意点

像我上面的跨容器调用的时候,将每一个容器都放在同一个文件中了,最好的做法需要将每个容器单独写一个文件。

最后一个啦,搬运突然觉得太累了~~

1、Getters

① getter 声明与使用

Getter 完全等同于 Store 状态的计算属性;Getters 函数的第一个参数是 state 对象;

在src/store/index.ts文件中去写一个getters,来体验一下:

import { defineStore } from 'pinia' // 导入 pinia 中的功能模块

// 创建容器
// 参数一:容器名
// 参数二:容器的内容
const useMainStore =  defineStore('main', {
  // pinia 状态管理的数据,通过箭头函数返回一个对象
  // 相当于 vue 中的 data 数据
  state: () => {
    return {
      message1: 'Hello',
      message2: 'Pinia',
      count: 1,
      number: 666
    }
  },

  // 相当于 vue 中的 computed 计算属性
  getters: {
    // 定义的 getters,第一个参数就是该容器的 state
    comp(state) {
      return ++state.count
    }
  },

  // 相当于 vue 中的 methods 方法
  actions: {
    // 1、count 加法方法
    addCount() {
      this.count++
    }
  }
})

// 导出容器
export {
  useMainStore
}

在页面组件中可以直接去调用该容器的getters方法:

<template>
  <!-- 后面相同的调用,都是基于第一次计算的值 -->
  <p>{{ mainStore.comp }}</p>
  <p>{{ mainStore.comp }}</p>
  <p>{{ mainStore.comp }}</p>
</template>

<script setup lang="ts">
// 导入 pinia 实例
import { useMainStore } from '../store/index'

// 实例化容器
const mainStore = useMainStore()
</script>

<style scoped></style>

可以查看页面效果,只会调用该getters一次,和计算属性一样,都存在缓存:

② 将参数传递给 getters (回调函数)

Getter 只是幕后的计算属性,因此无法向它们传递任何参数。但是,您可以从 getter 返回一个函数来接受任何参数,请注意,执行此操作时,getter 不再缓存,它们只是您调用的函数。但是,您可以在 getter 本身内部缓存一些结果,这并不常见,但性能更高:

import { defineStore } from 'pinia' // 导入 pinia 中的功能模块

// 创建容器
// 参数一:容器名
// 参数二:容器的内容
const useMainStore =  defineStore('main', {
  // pinia 状态管理的数据,通过箭头函数返回一个对象
  // 相当于 vue 中的 data 数据
  state: () => {
    return {
      message1: 'Hello',
      message2: 'Pinia',
      count: 1,
      number: 666
    }
  },

  // 相当于 vue 中的 computed 计算属性
  getters: {
    // 定义的 getters,第一个参数就是该容器的 state
    comp(state) {
      return (num: number) => {
        return state.count + num
      }
    }
  },

  // 相当于 vue 中的 methods 方法
  actions: {
    // 1、count 加法方法
    addCount() {
      this.count++
    }
  }
})

// 导出容器
export {
  useMainStore
}

然后在组件中使用它们:

<template>
  <!-- 后面相同的调用,都是基于第一次计算的值 -->
  <p>{{ mainStore.comp(2) }}</p>
  <p>{{ mainStore.comp(3) }}</p>
  <p>{{ mainStore.comp(4) }}</p>
</template>

<script setup lang="ts">
// 导入 pinia 实例
import { useMainStore } from '../store/index'

// 实例化容器
const mainStore = useMainStore()
</script>

<style scoped></style>

可以查看页面的效果:

注意: 如果给getters传参,返回的函数不会被缓存

③ getter中的this

getter中同样可以使用 this,但是 TS 无法推导类型,需要手动指定返回值类型:

// 类似于组件的computed,用来封装计算属性,有缓存的功能
getters: {
  // 传入 state [可选参数]
  // computeds(state){
  //   console.log('getter运行了')
  //   return state.count + 1
  // }

  // !getter 中同样可以使用 this ,但是 TS 无法推导类型,需要手动指定返回值类型
  computeds():number {
    console.log('getter运行了')
    return this.count + 1
  }
},

④ 访问其它getters

与计算属性一样,您可以组合多个 getter。通过此访问任何其他 getter:

getters: {
  computeds():number{
    console.log('getter运行了')
    return this.count+2
  },

  double():number{
    this.foo = this.computeds * 2 
    return this.foo
  }
},

⑤ 访问其它容器的actions 或getter

直接导入并实例化容器后,使用即可,与前面的跨容器调用一致,不再展示示例代码

———————————————— 版权声明:本文为CSDN博主「凉爽爽爽爽爽爽爽爽爽」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:blog.csdn.net/qq_45770253…