程序员scholar 程序员scholar
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
npm

(进入注册为作者充电)

  • Vue2

  • Vue3

    • 初识Vue3
    • 组合式API
    • Vue3中的data数据
    • vue3 的 script标签
    • setup函数中执行顺序
    • toRefs() 与 toRef()
    • computed()
    • Vue3弱化this
    • watch() 和 watchEffec()
    • define函数用法
    • defineExpose() 和 ref 属性
    • vue.config.js
    • 生命周期
    • Vue3全局API调用
    • 自定义 Hook
    • 传递数据(props)
    • 路由
    • pinia
      • 1. Pinia是什么?
      • 2. 搭建 Pinia 环境
        • 安装 Pinia
        • 在 src/main.ts 中配置 Pinia
      • 3. Pinia 的基础概念
        • 1. Store
        • 2. State
        • 3. Getters
        • 4. Actions
        • 4. 完整实例
        • 4.1 创建 Store
        • 4.2 在组件中使用 Store
      • 4. 修改数据(Pinia 中的三种方式)
        • 1. 直接修改
        • 2. 批量修改
        • 3. 通过 Actions 修改
      • 5. storeToRefs
      • 6. $subscribe
      • 7. Store 组合式写法
        • 组合式 API 基础语法
        • 1. 定义 state
        • 2. 定义 getters
        • 3. 定义 actions
        • 使用组合式 API 定义 Store
        • 在组件中使用 Store
    • 组件通信
    • getCurrentInstance() 和 nextTick()
    • Vue3 新组件
  • vue3 + TS 项目集成

  • Vue全家桶
  • Vue3
scholar
2024-08-05
目录

pinia

# pinia

# 1. Pinia是什么?

Pinia 是 Vue.js 的新一代状态管理库,它是 Vuex 的替代品,提供了更简单、灵活和强大的状态管理功能。与 Vuex 类似,Pinia 也提供了 state、getters 和 actions 来管理状态,但它在 API 设计和性能优化方面进行了改进。

Pinia 官方文档: Pinia Documentation (opens new window)

# 2. 搭建 Pinia 环境

要在 Vue 项目中使用 Pinia,需要先进行一些安装和配置步骤。

# 安装 Pinia

使用 npm 安装 Pinia:

npm install pinia
1

# 在 src/main.ts 中配置 Pinia

在 Vue 应用的入口文件中创建并使用 Pinia 实例。

// main.ts
import { createApp } from 'vue';
import App from './App.vue';

/* 引入 createPinia,用于创建 Pinia 实例 */
import { createPinia } from 'pinia';

/* 创建 Pinia 实例 */
const pinia = createPinia();

/* 创建 Vue 应用实例 */
const app = createApp(App);

/* 注册 Pinia 插件到 Vue 应用 */
app.use(pinia);

/* 挂载应用 */
app.mount('#app');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

配置完成后,你可以在开发者工具中看到 Pinia 选项卡。

pinia devtools

# 3. Pinia 的基础概念

Pinia 主要包括三个核心概念:state、getters 和 actions。这些概念分别对应于组件中的数据、计算属性和方法。

# 1. Store

  • 概念:Store 是 Pinia 的核心,它是一个用于存储和管理应用状态的容器。每个 Store 都有一个唯一的标识符(id),并包含三个主要部分:state、getters 和 actions。

  • 创建 Store:使用 defineStore 函数来定义和创建一个 Store。

    import { defineStore } from 'pinia';
    
    // 定义一个名为 'main' 的 Store
    export const useMainStore = defineStore('main', {
      // state:存储状态的函数
      state: () => ({
        // 在 state 中存储应用的状态
      }),
      // getters:计算属性的函数集合
      getters: {
        // 在 getters 中定义计算属性
      },
      // actions:处理业务逻辑和异步操作的函数集合
      actions: {
        // 在 actions 中定义业务逻辑和异步操作
      }
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

# 2. State

  • 概念:state 是 Store 中存储数据的地方,它类似于组件中的 data。所有的状态都定义在 state 函数返回的对象中。

  • 语法和使用:

    import { defineStore } from 'pinia';
    
    export const useCounterStore = defineStore('counter', {
      // state:返回一个对象,包含所有状态数据
      state: () => ({
        count: 0, // 定义一个 count 状态
        user: { name: 'Alice', age: 25 } // 定义一个 user 对象
      })
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  • 在组件中使用 state:

    在模板中可以直接使用 store.state 中的状态数据,在 script 标签中通过 Store 实例来访问状态。

    <template>
      <div>
        <p>Count: {{ counterStore.count }}</p> <!-- 模板中直接使用 -->
        <p>User: {{ counterStore.user.name }} ({{ counterStore.user.age }} years old)</p>
      </div>
    </template>
    
    <script setup>
    import { useCounterStore } from '@/store/counter';
    
    // 获取 Store 实例
    const counterStore = useCounterStore();
    
    // 在 script 标签中访问状态
    console.log(counterStore.count);
    console.log(counterStore.user.name);
    </script>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

# 3. Getters

  • 概念:getters 是 Store 中的计算属性,用于对状态进行派生计算或转换。它类似于组件中的 computed,但 getters 可以依赖于其他 getters。

  • 语法和使用:

    import { defineStore } from 'pinia';
    
    export const useCounterStore = defineStore('counter', {
      state: () => ({
        count: 0
      }),
      // getters:定义派生状态和计算属性
      getters: {
        // 计算 count 的平方
        squaredCount(state) {
          return state.count * state.count;
        }
      }
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
  • 在组件中使用 getters:

    在模板中可以直接使用 store.getters 中的派生状态和计算属性,在 script 标签中通过 Store 实例来访问 getters。

    <template>
      <div>
        <p>Squared Count: {{ counterStore.squaredCount }}</p> <!-- 模板中直接使用 -->
      </div>
    </template>
    
    <script setup>
    import { useCounterStore } from '@/store/counter';
    
    // 获取 Store 实例
    const counterStore = useCounterStore();
    
    // 在 script 标签中访问 getters
    console.log(counterStore.squaredCount);
    </script>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

# 4. Actions

  • 概念:actions 用于封装业务逻辑和处理异步操作。与 getters 和 state 不同,actions 既可以修改状态,也可以执行副作用。

  • 语法和使用:

    import { defineStore } from 'pinia';
    
    export const useCounterStore = defineStore('counter', {
      state: () => ({
        count: 0
      }),
      // actions:定义用于处理业务逻辑和异步操作的函数
      actions: {
        // 同步操作
        increment() {
          this.count++;
        },
        // 异步操作
        async fetchCount() {
          const response = await fetch('/api/count');
          const data = await response.json();
          this.count = data.count;
        }
      }
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
  • 在组件中使用 actions:

    在模板中可以绑定按钮点击事件来调用 actions,在 script 标签中通过 Store 实例来调用 actions。

    <template>
      <div>
        <p>Count: {{ counterStore.count }}</p>
        <button @click="counterStore.increment">Increment</button> <!-- 绑定按钮事件 -->
        <button @click="fetchCount">Fetch Count</button>
      </div>
    </template>
    
    <script setup>
    import { useCounterStore } from '@/store/counter';
    
    // 获取 Store 实例
    const counterStore = useCounterStore();
    
    // 调用 actions 中的异步方法
    async function fetchCount() {
      await counterStore.fetchCount();
    }
    </script>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

# 4. 完整实例

以下是一个完整的实例,展示如何在 Vue.js 项目中使用 Pinia 来管理状态。

# 4.1 创建 Store

在 src/store 目录下创建一个 Store 文件 counter.ts。

// src/store/counter.js
import { defineStore } from 'pinia';

// 定义并导出一个名为 'counter' 的 Store
export const useCounterStore = defineStore('counter', {
  // state:存储状态
  state: () => ({
    count: 0 // 初始值
  }),
  // getters:计算属性
  getters: {
    // 获取 count 的平方
    squaredCount(state) {
      return state.count * state.count;
    }
  },
  // actions:业务逻辑
  actions: {
    // 增加 count
    increment() {
      this.count++;
    },
    // 异步操作,模拟获取数据
    async fetchCount() {
      const response = await fetch('/api/count');
      const data = await response.json();
      this.count = data.count;
    }
  }
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 4.2 在组件中使用 Store

在组件中通过调用 Store 来获取和使用其中的状态、计算属性和方法。

<template>
  <div>
    <h2>当前计数:{{ counterStore.count }}</h2>
    <p>平方:{{ counterStore.squaredCount }}</p>
    <button @click="counterStore.increment">增加</button>
    <button @click="fetchCount">获取计数</button>
  </div>
</template>

<script setup>
import { useCounterStore } from '@/store/counter';

// 获取 counterStore 实例
const counterStore = useCounterStore();

// 调用 actions 中的异步方法
async function fetchCount() {
  await counterStore.fetchCount();
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 4. 修改数据(Pinia 中的三种方式)

在 Pinia 中,我们可以通过多种方式来修改存储在 Store 中的数据,包括直接修改、批量修改和通过 actions 修改。这三种方式各有优劣,适合不同的场景需求。

# 1. 直接修改

  • 概念:直接修改是最简单的方式,可以直接赋值修改 state 中的属性。这种方式适用于简单的状态更新。

  • 示例:

    // 假设我们有一个 countStore
    import { useCounterStore } from '@/store/counter';
    
    // 获取 Store 实例
    const counterStore = useCounterStore();
    
    // 直接修改 state 中的 sum 属性
    counterStore.sum = 666; // 直接赋值修改 sum
    
    1
    2
    3
    4
    5
    6
    7
    8
  • 优点:简单直接,适用于简单的数据更新。

  • 缺点:不适合复杂的业务逻辑或需要批量更新的场景。

# 2. 批量修改

  • 概念:使用 $patch 方法可以批量修改 state,同时更新多个属性。这种方式适用于需要同时更新多个属性的场景。

  • 语法:

    // 使用 $patch 批量更新 state
    counterStore.$patch({
      sum: 999,
      school: 'atguigu'
    });
    
    1
    2
    3
    4
    5
  • 优点:方便进行批量更新,避免多次赋值。

  • 缺点:不适用于包含复杂业务逻辑的场景。

# 3. 通过 Actions 修改

  • 概念:通过 actions 可以封装业务逻辑,并在其中修改 state。这种方式适用于需要执行复杂逻辑或异步操作的场景。

  • 语法:

    在 Store 中定义 actions:

    import { defineStore } from 'pinia';
    
    export const useCounterStore = defineStore('counter', {
      state: () => ({
        sum: 0
      }),
      actions: {
        // 增加方法,带有业务逻辑
        increment(value: number) {
          if (this.sum < 10) {
            this.sum += value; // 修改 sum 属性
          }
        },
        // 减少方法,带有业务逻辑
        decrement(value: number) {
          if (this.sum > 1) {
            this.sum -= value; // 修改 sum 属性
          }
        }
      }
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
  • 在组件中调用 actions:

    在组件中,通过 Store 实例来调用定义的 actions。

    <template>
      <div>
        <h2>当前求和为:{{ counterStore.sum }}</h2>
        <button @click="increment(2)">增加</button>
        <button @click="decrement(1)">减少</button>
      </div>
    </template>
    
    <script setup>
    import { useCounterStore } from '@/store/counter';
    
    // 获取 counterStore 实例
    const counterStore = useCounterStore();
    
    // 定义方法来调用 actions
    function increment(value) {
      counterStore.increment(value); // 调用 increment 方法
    }
    
    function decrement(value) {
      counterStore.decrement(value); // 调用 decrement 方法
    }
    </script>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
  • 优点:可以封装复杂逻辑,支持异步操作,易于测试和维护。

  • 缺点:需要额外的代码编写,增加了一定的复杂性。

总结

  • 直接修改:简单快捷,适用于简单的状态更新。
  • 批量修改:使用 $patch,适合同时更新多个属性。
  • 通过 Actions 修改:封装复杂逻辑,支持异步操作,适用于复杂场景。

# 5. storeToRefs

storeToRefs 是 Pinia 提供的一个辅助函数,用于将 Store 中的状态和计算属性转换为 Vue 的 ref 对象。这样可以在模板中使用响应式数据时避免使用 .value,并保持与其他 ref 数据一致的行为。

使用 storeToRefs

  • 目的:将 Store 中的状态和计算属性转换为 ref,以便在模板中更方便地使用。
  • 注意:storeToRefs 只会将 Store 中的状态和计算属性转换为 ref,而不会影响 Store 的结构或行为。
  • 一旦使用 storeToRefs 转换为 ref,在模板中就不需要通过 Store 实例访问状态和计算属性。这提高了代码的可读性和维护性。

下面示例展示了如何在组件中使用 storeToRefs 将 Store 中的数据转换为 ref,并在模板中使用这些 ref 数据。

<template>
  <div class="count">
    <!-- 在模板中直接使用 sum,无需使用 .value -->
    <h2>当前求和为:{{ sum }}</h2>
  </div>
</template>

<script setup lang="ts" name="Count">
import { useCountStore } from '@/store/count';
import { storeToRefs } from 'pinia';

// 获取 countStore 实例
const countStore = useCountStore();

// 使用 storeToRefs 转换 countStore 中的状态和计算属性
const { sum } = storeToRefs(countStore);

</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

总结

  • 便捷性:storeToRefs 提供了一种便捷的方式,将 Store 中的数据转换为 ref,使得在模板中使用更加直观。
  • 一致性:通过将 Store 的状态和计算属性转换为 ref,可以在组件中保持对响应式数据的一致处理方式。
  • 应用场景:特别适用于大型组件或应用中,减少模板中对 .value 的频繁使用,提高代码可读性。

# 6. $subscribe

Pinia 提供了 $subscribe() 方法用于侦听 Store 的状态变化。这允许你在状态变化时执行特定的逻辑,例如同步数据到 localStorage 或执行其他副作用。

语法:

store.$subscribe((mutation, state) => {
  // mutation: 包含有关变更的信息
  // state: 当前的状态
});
1
2
3
4
  • mutation: 一个对象,包含有关变更的信息,通常包括变更类型和变更目标。
  • state: 当前 Store 的状态。

下面示例展示了如何使用 $subscribe 监听状态变化并将其存储在 localStorage 中。

// 在组件或其他合适的地方调用
talkStore.$subscribe((mutation, state) => {
  console.log('状态变更:', mutation, state); // 打印变更信息和当前状态
  // 将 talkList 存储到 localStorage 中
  localStorage.setItem('talk', JSON.stringify(state.talkList));
});
1
2
3
4
5
6
  • 监听变化:通过 $subscribe 可以监听 Store 的状态变化,以便在每次变化时执行自定义逻辑。
  • 持久化数据:在状态变化时,可以将数据同步到 localStorage 或其他存储介质,以实现数据持久化。

# 7. Store 组合式写法

Pinia 支持组合式 API 语法来定义 Store,使得状态管理更加灵活和清晰。这种写法类似于 Vue 3 的组合式 API。

在 Pinia 中,使用组合式 API 定义 state、getters 和 actions 提供了一种更灵活和模块化的方式来管理应用的状态。这种写法与 Vue 3 的组合式 API 一致,利用函数来定义和返回状态及行为。

# 组合式 API 基础语法

# 1. 定义 state

在组合式 API 中,使用 reactive 或 ref 定义 state。

import { reactive, ref } from 'vue';

// 定义一个响应式对象作为状态
const state = reactive({
  count: 0, // 基本数据类型
  user: { name: 'Alice', age: 25 } // 对象类型
});

// 也可以使用 ref 定义单一状态
const count = ref(0);
1
2
3
4
5
6
7
8
9
10

# 2. 定义 getters

getters 是派生状态,用于计算和转换 state 中的数据。

import { computed } from 'vue';

// 定义一个计算属性
const squaredCount = computed(() => state.count * state.count);

// 如果使用 ref 作为状态,也可以这样定义 getters
const doubleCount = computed(() => count.value * 2);
1
2
3
4
5
6
7

# 3. 定义 actions

actions 是处理业务逻辑和异步操作的地方,可以直接修改 state。

// 定义同步和异步的动作
function increment() {
  state.count++;
}

async function fetchCount() {
  const response = await fetch('/api/count');
  const data = await response.json();
  state.count = data.count;
}
1
2
3
4
5
6
7
8
9
10

# 使用组合式 API 定义 Store

可以使用 defineStore 函数结合上述语法来定义一个 Store。

import { defineStore } from 'pinia';
import { reactive, ref, computed } from 'vue';

// 使用 defineStore 定义 Store
export const useCounterStore = defineStore('counter', () => {
  // 使用 reactive 定义 state
  const state = reactive({
    count: 0,
    user: { name: 'Alice', age: 25 }
  });

  // 使用 computed 定义 getters
  const squaredCount = computed(() => state.count * state.count);

  // 定义 actions
  function increment() {
    state.count++;
  }

  async function fetchCount() {
    const response = await fetch('/api/count');
    const data = await response.json();
    state.count = data.count;
  }

  // 返回 state、getters 和 actions
  return {
    state,
    squaredCount,
    increment,
    fetchCount
  };
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# 在组件中使用 Store

<template>
  <div>
    <h2>当前计数:{{ count }}</h2>
    <p>平方:{{ squaredCount }}</p>
    <button @click="increment">增加</button>
    <button @click="fetchCount">获取计数</button>
  </div>
</template>

<script setup>
import { useCounterStore } from '@/store/counter';

// 获取 Store 实例
const counterStore = useCounterStore();

// 解构获取 state 和 actions
const { count, squaredCount, increment, fetchCount } = counterStore;
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

总结

  • 组合式 API:使用 reactive 和 ref 定义 state,使用 computed 定义 getters,使用函数定义 actions。
  • 模块化:将所有的状态、计算属性和业务逻辑放在一个函数中定义和返回,使得代码结构更加清晰和模块化。
  • 灵活性:可以随意组合和调用状态、计算属性和动作,提高代码的可维护性和可读性。
编辑此页 (opens new window)
上次更新: 2025/02/01, 02:18:15
路由
组件通信

← 路由 组件通信→

Theme by Vdoing | Copyright © 2019-2025 程序员scholar
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式