在大型应用程序中,由于多个组件都有自己的状态并且相互依赖,使得状态管理变得很复杂。人们常常忽略的是,Vue 实例中的真实数据源是原始数据对象 - Vue 实例只是对它进行了代理访问。因此,如果您有一段需要多个实例共享的状态,您应该避免重复它,而是通过引用来共享。
如果组件之间想要共享同一个状态,我们推荐您使用 Pinia。开始之前我们希望您已经了解了 Pinia 的文档。配合 Vue dev-tools 浏览器扩展一起使用时,它还有像时间旅行调试这样的强大功能。
我们不会详细讨论如何配置或使用 Pinia,因为它有很棒的文档。相反,我们将向您展示在 Quasar 项目中使用它时,目录结构是什么样子的。
当您使用 Quasar CLI 创建项目时,它会询问是否要添加 Pinia 并为您准备好所有必要的配置,包括帮您创建好 /src/stores 目录结构,在其中包括了与 Pinia 相关的必要的代码。
如果您在创建项目时未选择添加 Pinia,但想要为已有项目添加 Pinia 支持,那么您需要做的只是参考下一节中的步骤并创建 src/stores/index.js 文件(运行 quasar new store <name> 时会自动创建):
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 文件:
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 stores 中访问 router
只需在 Pinia stores 中使用 this.router 即可访问 router。
示例:
import { defineStore } from 'pinia'
export const useWhateverStore = defineStore('whatever', {
// ...
actions: {
whateverAction () { // 不要使用箭头函数
this.router.push('...')
}
}
}