在大型应用程序中,由于多个状态片段分散在许多组件中以及它们之间的交互,状态管理通常会变得复杂。通常会忽略 Vue 实例中真相的来源是原始数据对象 - Vue 实例只是代理对其的访问。因此,如果您有一个应该由多个实例共享的状态片段,则应避免复制它并通过身份共享它。
如果您希望组件共享状态,建议的方法是使用 Pinia。在深入了解之前,请查看其 文档。当与 Vue 开发者工具 浏览器扩展一起使用时,它具有一个很棒的功能,例如时光倒流调试。
我们不会详细介绍如何配置或使用 Pinia,因为它有很好的文档。相反,我们将向您展示在 Quasar 项目中使用它时的文件夹结构。
当您搭建 Quasar 项目文件夹时,可以选择添加 Pinia。它会为您创建所有必要的配置。例如,创建 /src/stores
,它处理您需要的与 Pinia 相关的所有代码。
如果您在项目创建期间没有选择 Pinia 选项,但想稍后添加它,那么您只需检查下一部分并创建 src/stores/index.[js|ts]
文件(当您运行 quasar new store <name>
时会自动创建)。
import { store } from 'quasar/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 store((/* { 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|ts]
文件
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>
<!-- Option 1 -->
<div>Direct store</div>
<!-- Read the state value directly -->
<div>{{ store.counter }}</div>
<!-- Use getter directly -->
<div>{{ store.doubleCount }}</div>
<!-- Manipulate state directly -->
<q-btn @click="store.counter--">-</q-btn>
<!-- Use an action -->
<q-btn @click="store.increment()">+</q-btn>
</div>
<div>
<!-- Option 2 -->
<div>Indirect store</div>
<!-- Use the computed state -->
<div>{{ count }}</div>
<!-- Use the computed getter -->
<div>{{ doubleCountValue }}</div>
<!-- Use the exposed function -->
<q-btn @click="decrementCount()">-</q-btn>
<!-- Use the exposed function -->
<q-btn @click="incrementCount()">+</q-btn>
</div>
<div>
<!-- Option 3 -->
<div>Destructured store</div>
<!-- Use the destructured state -->
<div>{{ counter }}</div>
<!-- Use the destructured getter -->
<div>{{ doubleCount }}</div>
<!-- Manipulate state directly-->
<q-btn @click="counter--">-</q-btn>
<!-- Use an 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();
// Option 2: use computed and functions to use the store
const count = computed(() => store.counter);
const doubleCountValue = computed(() => store.doubleCount);
const incrementCount = () => store.increment(); // use action
const decrementCount = () => store.counter--; // manipulate directly
// Option 3: use destructuring to use the store in the template
const { counter, doubleCount } = storeToRefs(store); // state and getters need "storeToRefs"
const { increment } = store; // actions can be destructured directly
return {
// Option 1: return the store directly and couple it in the template
store,
// Option 2: use the store in functions and compute the state to use in the template
count,
doubleCountValue,
incrementCount,
decrementCount,
// Option 3: pass the destructed state, getters and actions to the template
counter,
increment,
doubleCount,
};
},
};
</script>
在 Pinia store 中访问路由
只需在 Pinia store 中使用 this.router
即可访问路由。
这是一个例子
import { defineStore } from 'pinia'
export const useWhateverStore = defineStore('whatever', {
// ...
actions: {
whateverAction () {
this.router.push('...')
}
}
}