← 目录从零开始学vue3

状态管理(Pinia)

2024/9/17

Vue 3 的 setup 函数与 Pinia 是非常理想的搭档,Pinia 是 Vue 的新一代状态管理库,相比 Vuex,更加轻量和直观。Pinia 支持完整的 TypeScript 类型推断,并且它充分利用了 Vue 3 的 Composition API 特性。

下面将详细介绍 Pinia 的状态管理,并提供详细示例说明。

一、Pinia 的安装与基本配置

1. 安装 Pinia

首先,在你的 Vue 3 项目中安装 Pinia:

bash
npm install pinia

2. 创建 Pinia 实例

在 main.js 中创建 Pinia 实例,并将其注册到 Vue 应用中:

js
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';

const app = createApp(App);
const pinia = createPinia(); // 创建 Pinia 实例

app.use(pinia); // 使用 Pinia
app.mount('#app');

二、Pinia Store 的使用

Pinia 的 store 概念类似于 Vuex,但它更加简洁。下面是如何使用 Pinia 创建和使用 store 的详细步骤。

1. 创建 Store

创建一个 store 文件夹,然后在其中创建 counterStore.js 文件,定义一个 counter 状态管理。

js
import { defineStore } from 'pinia';

// 定义 store
export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0
  }),
  actions: {
    increment() {
      this.count++;
    },
    decrement() {
      this.count--;
    }
  },
  getters: {
    doubleCount: (state) => state.count * 2
  }
});
  • state:用于定义存储的数据。
  • actions:用于定义可以操作 state 的方法,支持同步和异步操作。
  • getters:用于计算衍生的状态。

2. 在组件中使用 Pinia Store

在组件中,通过 useCounterStore 来访问和操作 store。

vue
<template>
  <div>
    <h1>计数:{{ count }}</h1>
    <h2>双倍计数:{{ doubleCount }}</h2>
    <button @click="increment">增加</button>
    <button @click="decrement">减少</button>
  </div>
</template>

<script>
import { defineComponent } from 'vue';
import { useCounterStore } from '../stores/counterStore';

export default defineComponent({
  setup() {
    const counterStore = useCounterStore(); // 使用 store

    return {
      count: counterStore.count,
      doubleCount: counterStore.doubleCount,
      increment: counterStore.increment,
      decrement: counterStore.decrement
    };
  }
});
</script>

在 setup 函数中,通过 useCounterStore 获取到 store 实例,然后使用它的 state、getters 和 actions。

三、详细示例说明

1. 访问和修改状态(State)

Pinia 的状态管理与 Vuex 类似,但在使用上更为简洁。在组件中直接使用 store.state 或通过解构 store 来访问 state,并可以通过 actions 来改变状态。

示例:

vue
<template>
  <div>
    <h1>当前计数:{{ count }}</h1>
    <button @click="increment">增加</button>
  </div>
</template>

<script>
import { defineComponent } from 'vue';
import { useCounterStore } from '../stores/counterStore';

export default defineComponent({
  setup() {
    const counterStore = useCounterStore();

    return {
      count: counterStore.count,
      increment: counterStore.increment
    };
  }
});
</script>

2. 使用计算属性(Getters)

Pinia 的 getters 提供类似于计算属性的功能,可以通过计算当前状态来生成衍生数据。

vue
<template>
  <div>
    <h1>当前计数:{{ count }}</h1>
    <h2>双倍计数:{{ doubleCount }}</h2>
  </div>
</template>

<script>
import { defineComponent } from 'vue';
import { useCounterStore } from '../stores/counterStore';

export default defineComponent({
  setup() {
    const counterStore = useCounterStore();

    return {
      count: counterStore.count,
      doubleCount: counterStore.doubleCount // 使用 getters
    };
  }
});
</script>

3. 异步 Actions

Pinia 的 actions 不仅可以处理同步逻辑,还可以处理异步操作。

示例:

js
export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0
  }),
  actions: {
    async incrementAsync() {
      await new Promise((resolve) => setTimeout(resolve, 1000));
      this.count++;
    }
  }
});

在组件中调用异步的 action:

vue
<template>
  <div>
    <h1>当前计数:{{ count }}</h1>
    <button @click="incrementAsync">异步增加</button>
  </div>
</template>

<script>
import { defineComponent } from 'vue';
import { useCounterStore } from '../stores/counterStore';

export default defineComponent({
  setup() {
    const counterStore = useCounterStore();

    return {
      count: counterStore.count,
      incrementAsync: counterStore.incrementAsync
    };
  }
});
</script>

四、模块化 Store

Pinia 的模块化非常简单,类似于 Vuex 的模块化。在实际项目中,我们可以将 store 分割为多个模块,保持代码的清晰和维护性。

示例:用户 Store

js
// stores/userStore.js
import { defineStore } from 'pinia';

export const useUserStore = defineStore('user', {
  state: () => ({
    name: 'John Doe',
    age: 25
  }),
  actions: {
    setName(newName) {
      this.name = newName;
    }
  },
  getters: {
    capitalizedUserName(state) {
      return state.name.toUpperCase();
    }
  }
});

在组件中使用:

vue
<template>
  <div>
    <h1>用户姓名:{{ name }}</h1>
    <h2>用户姓名(大写):{{ capitalizedUserName }}</h2>
    <input v-model="newName" placeholder="输入新姓名" />
    <button @click="setName(newName)">更新姓名</button>
  </div>
</template>

<script>
import { defineComponent, ref } from 'vue';
import { useUserStore } from '../stores/userStore';

export default defineComponent({
  setup() {
    const userStore = useUserStore();
    const newName = ref('');

    return {
      name: userStore.name,
      capitalizedUserName: userStore.capitalizedUserName,
      newName,
      setName: userStore.setName
    };
  }
});
</script>

五、Pinia 与 Vuex 的比较

  1. 更轻量:Pinia 的 API 更加简洁,它只提供了最核心的功能。
  2. TypeScript 支持更好:Pinia 内置了更好的类型推导支持。
  3. Vue 3 特性支持:Pinia 完全基于 Vue 3 的 Composition API 构建,结合 setup 使用非常自然。

六、总结

  • State 管理:通过 useStore 函数可以方便地在组件中访问和管理状态。
  • Getters:类似于计算属性,用于计算衍生状态。
  • Actions:处理业务逻辑、修改状态,支持异步操作。
  • 模块化:支持将 store 按照不同的业务模块划分,方便管理。

Pinia 提供了 Vue 3 项目中更加现代化、轻量且强大的状态管理方案,是 Vuex 的一个优秀替代品。