在大型应用中,由于多块状态散落在许多组件中以及它们之间的交互,状态管理往往变得复杂。人们常常忽略的是,Vue实例中的真实来源是原始数据对象–Vue实例只是代理了对它的访问。因此,如果你有一块应该被多个实例共享的状态,你应该避免重复它,而是通过身份来共享它。
如果你想让组件共享状态,推荐的方法是Pinia。在深入研究之前,请看一下它的文档。当与Vue dev-tools浏览器扩展一起使用时,它有一个很棒的功能,比如时间旅行调试。
我们不会详细介绍如何配置或使用Pinia,因为它有很棒的文档。相反,我们将向你展示在Quasar项目中使用它时,文件夹结构是什么样子的。
.
└── src/
└── stores/ # Pinia
├── index.js # Pinia initialization
├── <store> # Pinia store...
└── <store> # Pinia store...
当你为Quasar项目文件夹搭建脚手架时,你可以选择添加Pinia。它将为你创建所有必要的配置。比如创建/src/stores
,处理所有你需要的Pinia相关代码。
如果你在创建项目时没有选择Pinia选项,但想在以后添加它,那么你所需要做的就是检查下一节,并创建src/stores/index.[js|ts]
文件 (当你运行quasar new store <name>
时,它会自动创建):
// src/stores/index.js
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存储
通过Quasar CLI的"$ quasar new "命令,添加一个Pinia存储很容易。
$ quasar new store <store_name> [--format ts]
它将在/src/stores
中创建一个文件夹,以上述命令中的"store_name"命名。它将包含所有你需要的模板。
比方说,你想创建一个 "计数器 "Pinia商店。你发出$ quasar new store counter
。然后你注意到新创建的/src/stores/counter.[js|ts]
文件:
.
└── src/
└── stores/
├── index.js # Pinia initialization
└── counter.js # Pinia store
Pinia存储的例子:
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
counter: 0,
}),
getters: {
doubleCount: (state) => state.counter * 2,
},
actions: {
increment() {
this.counter++;
},
},
})
我们已经创建了新的Pinia存储,但我们还没有在我们的应用程序中使用它。在一个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存储的更多信息在这里.
在Pinia存储中访问路由器
只需在Pinia存储中使用this.router
就可以访问路由器。
下面是一个例子:
import { defineStore } from 'pinia'
export const useWhateverStore = defineStore('whatever', {
// ...
actions: {
whateverAction () {
this.router.push('...')
}
}
}