Vue3 专属状态管理工具。
挂载 Pinia
- 在
src/main.js
中导入并挂载 pinia。
import { createPinia } from "pinia";
app.use(createPinia());
- 创建
src/store
文件夹,并区分模块创建.js
文件,并配置页面的状态管理。
store id
可以写在defineStore()
的参数一,也可以写在对象里。
state
pinia 中的
store
是模块化的,根据不同的状态划分不同的模块。
- 创建
src/store/productStore.js
文件。
# 写法一
import { defineStore } from "pinia";
export const useProductStore = defineStore("ProductStore", {
// state 函数用于返回状态对象(数据)
state: () => {
return {
products: [],
};
},
...
});
- 页面引入
直接从 store 对象中将状态解构出来,会失去响应式,状态发送变化不会驱动视图更新,
storeToRefs
可以将这些数据转为响应式。
# actions > `actions` 相当于组件中的方法,它们可以使用 `defineStore()` 中的 `actions` 属性进行定义,并且非常适合定义业务逻辑。
# src/store/productStore.js
import { defineStore } from "pinia";
export const useProductStore = defineStore("ProductStore", {
// state 函数用于返回状态对象(数据)
state: () => {
return {
products: [],
};
},
// 既可以执行异步操作,也可以修改状态
actions: {
// 导入数据
async fill () {
// 异步加载模块
this.products = await (import("@/data/products.json").default);
},
// 修改金额
updateProductItemPrice(index) {
this.products[index].price += 1;
},
},
});
# src/store/cartStore.js
import { defineStore } from "pinia";
export const useCartStore = defineStore("cartStore", {
state: () => {
return {
// 购物车数据
items: [],
};
},
actions: {
// 点击事件:加入购物车
addItems(count, product) {
for(let i=0; i<count, i++) {
this.items.push(product);
};
},
},
});
<!-- App.vue -->
<script setup>
import { storeToRefs } from "pinia";
import { onMounted } from "vue";
import { useProductStore } from "./store/productStore.js";
import { useCartStore } from "./store/cartStore.js";
const productStore = useProductStore();
const { products } = storeToRefs(productStore);
const cartStore = useCartStore();
onMounted(() => {
// 页面加载完成,渲染数据
productStore.fill();
});
// 点击加入购物车
const addCart = (count, product) => {
cartStore.addItems(count, product)
}
</script>
<template>
<ul class="sm:flex flex-wrap lg:flex-nowrap gap-5">
<ProductCard v-for="product in products" :key="product.name"
:product="product"
@addToCart="addToCart($event, product)"
/>
</ul>
</template>
getters
getters
完全等同于 Store 状态的计算值。可以使用defineStore()
中的getters
属性定义。接受state
作为第一参数,推荐使用箭头函数,当使用箭头函数时this
指向为undefined
。
# src/store/cartStore.js
import { defineStore } from "pinia";
export const useCartStore = defineStore("cartStore", {
state: () => {
return {
// 购物车数据
items: [],
};
},
actions: {
// 点击事件:加入购物车
addItems(count, product) {
for(let i=0; i<count, i++) {
this.items.push(product);
};
},
},
getters: {
// 计算购物车的数量
count: (state) => state.items.length,
},
});
<!-- src/components/CartWidget.vue -->
<span class="cursor-pointer" @click="active = true">
<fa icon="shopping-cart" size="lg" class="text-gray-700" />
<div class="cart-count absolute">{{ cartStore.count }}</div>
</span>
plugins
由于有了底层 API 的支持,Pinia store 现在完全支持扩展。以下是你可以扩展的内容:
- 为 store 添加新的属性
- 定义 store 时增加新的选项
- 为 store 增加新的方法
- 包装现有的方法
- 改变甚至取消 action
- 实现副作用,如本地存储
- 仅应用插件于特定 store
插件是通过 pinia.use() 添加到 pinia 实例的。最简单的例子是通过返回一个对象将一个静态属性添加到所有 store。
import { createPinia } from 'pinia'
// 在安装此插件后创建的每个 store 中都会添加一个名为 `secret` 的属性。
// 插件可以保存在不同的文件中
function SecretPiniaPlugin() {
return { secret: 'the cake is a lie' }
}
const pinia = createPinia()
// 将该插件交给 Pinia
pinia.use(SecretPiniaPlugin)
// 在另一个文件中
const store = useStore()
store.secret // 'the cake is a lie'
这对添加全局对象很有用,如路由器、modal 或 toast 管理器。