为什么捐赠
API 浏览器
联系站长
Quasar CLI with Vite - @quasar/app-vite
使用 Pinia 管理状态

在大型应用程序中,由于多个组件都有自己的状态并且相互依赖,使得状态管理变得很复杂。人们常常忽略的是,Vue 实例中的真实数据源是原始数据对象 - Vue 实例只是对它进行了代理访问。因此,如果您有一段需要多个实例共享的状态,您应该避免重复它,而是通过引用来共享。

如果组件之间想要共享同一个状态,我们推荐您使用 Pinia。开始之前我们希望您已经了解了 Pinia 的文档。配合 Vue dev-tools 浏览器扩展一起使用时,它还有像时间旅行调试这样的强大功能。

我们不会详细讨论如何配置或使用 Pinia,因为它有很棒的文档。相反,我们将向您展示在 Quasar 项目中使用它时,目录结构是什么样子的。

index.js
# (or .ts) Pinia initialization
<store>
# Pinia store...
<store>
# Pinia store...

当您使用 Quasar CLI 创建项目时,它会询问是否要添加 Pinia 并为您准备好所有必要的配置,包括帮您创建好 /src/stores 目录结构,在其中包括了与 Pinia 相关的必要的代码。

如果您在创建项目时未选择添加 Pinia,但想要为已有项目添加 Pinia 支持,那么您需要做的只是参考下一节中的步骤并创建 src/stores/index.js 文件(运行 quasar new store <name> 时会自动创建):

/src/stores/index.js

import { defineStore } from "#q-app/wrappers";
import { createPinia } from "pinia";

/*
 * If not building with SSR mode, you can
 * directly export the Store instantiation;
 *
 * The function below can be async too; either use
 * async/await or return a Promise which resolves
 * with the Store instance.
 */

export default defineStore((/* { ssrContext } */) => {
  const pinia = createPinia();

  // You can add Pinia plugins here
  // pinia.use(SomePiniaPlugin)

  return pinia;
});

添加一个 Pinia store

您可以使用 Quasar CLI 提供的 $ quasar new 命令快捷的添加一个 Pinia store:

$ quasar new store <store_name> [--format ts]

上面的命令将会在 /src/stores 目录下创建一个名为 “store_name” 的文件,它将包含您需要的所有模板文件。

我们来示例如何创建一个名为 “counter” 的 Pinia store。您需要运行 $ quasar new store counter 命令。它会帮您创建 /src/stores/counter.js 文件:

index.js
# (or .ts) Pinia initialization
counter.js
# (or .ts) Pinia store

Pinia store 示例:

import { defineStore } from "pinia";

export const useCounterStore = defineStore("counter", {
  state: () => ({
    counter: 0,
  }),
  getters: {
    doubleCount: (state) => state.counter * 2,
  },
  actions: {
    increment() {
      this.counter++;
    },
  },
});

我们创建了一个新的 Pinia store,但是我们还未在项目中使用它。在一个 Vue 文件中:

<template>
  <div>
    <!-- 方式 1 -->
    <div>直接使用 store</div>
    <!-- 直接读取 state 的值 -->
    <div>{{ store.counter }}</div>
    <!-- 直接使用 getter -->
    <div>{{ store.doubleCount }}</div>

    <!-- 直接修改 state -->
    <q-btn @click="store.counter--">-</q-btn>
    <!-- 使用 action -->
    <q-btn @click="store.increment()">+</q-btn>
  </div>

  <div>
    <!-- 方式 2 -->
    <div>间接使用 store</div>
    <!-- 使用计算属性访问 state -->
    <div>{{ count }}</div>
    <!-- 使用计算属性访问 getter -->
    <div>{{ doubleCountValue }}</div>

    <!-- 使用封装的函数 -->
    <q-btn @click="decrementCount()">-</q-btn>
    <!-- 使用封装的函数 -->
    <q-btn @click="incrementCount()">+</q-btn>
  </div>

  <div>
    <!-- 方式 3 -->
    <div>解构 store</div>
    <!-- 使用解构后的 state -->
    <div>{{ counter }}</div>
    <!-- 使用解构后的 getter -->
    <div>{{ doubleCount }}</div>

    <!-- 直接修改 state -->
    <q-btn @click="counter--">-</q-btn>
    <!-- 使用 action -->
    <q-btn @click="increment()">+</q-btn>
  </div>
</template>

<script>
  import { computed } from "vue";
  import { useCounterStore } from "stores/counter";
  import { storeToRefs } from "pinia";

  export default {
    setup() {
      const store = useCounterStore();

      // 方式 2: 使用 computed 和函数来使用 store
      const count = computed(() => store.counter);
      const doubleCountValue = computed(() => store.doubleCount);
      const incrementCount = () => store.increment(); // 使用 action
      const decrementCount = () => store.counter--; // 直接修改

      // 方式 3: 使用解构来在模板中使用 store
      const { counter, doubleCount } = storeToRefs(store); // state 和 getters 需要 "storeToRefs"
      const { increment } = store; // actions 可以直接解构

      return {
        // 方式 1: 直接返回 store 实例并在模板中使用
        store,

        // 方式 2: 在函数中使用 store 并将计算属性传给模板
        count,
        doubleCountValue,
        incrementCount,
        decrementCount,

        // 方式 3: 将解构的 state、getters 和 actions 传给模板
        counter,
        increment,
        doubleCount,
      };
    },
  };
</script>

关于定义一个 Pinia store 的更多参考

在 Pinia stores 中访问 router

只需在 Pinia stores 中使用 this.router 即可访问 router。

示例:

import { defineStore } from 'pinia'

export const useWhateverStore = defineStore('whatever', {
  // ...
  actions: {
    whateverAction () { // 不要使用箭头函数
      this.router.push('...')
    }
  }
}