一、 Pinia 是什么?
Pinia 是一个专为 Vue.js 设计的状态管理库。它提供了一种简单且优雅的方式来管理应用程序的状态,并且与 Vue 3 生态系统无缝集成。Pinia 的设计目标是提供一种现代化的状态管理解决方案,使得开发者能够更轻松地管理应用程序中的状态,并且在面对大型应用或复杂状态管理需求时保持高效。
有关状态的概念以及与Vuex之间的对比可参考: Vue核心知识点 - VueX-CSDN博客
二、Pinia 核心知识点
官网:简介 | Pinia
三、Pinia 如何在项目中使用(以 vue3 组合式api + ts 举例)
步骤一、下载 pinia
yarn add pinia
# 或者使用 npm
npm install pinia
步骤二、创建 Pinia Store
// src/store/counter.ts
import { defineStore } from "pinia";
export const useCounterStore = defineStore("counter", () => {
// ref变量 → state 属性
const count = ref(0);
// computed计算属性 → getters
const double = computed(() => {
return count.value * 2;
});
// function函数 → actions
function increment() {
count.value++;
}
return { count, double, increment };
});
步骤三、在 main.ts 全局注册 Pinia
import { createApp } from 'vue'
import App from './App.vue'
// 导入 createPinia 函数
import { createPinia } from 'pinia';
const app = createApp(App)
// 全局注册 pinia
app.use(createPinia())
app.mount('#app')
步骤四、组件中使用 Pinia Store
父组件
<script setup lang="ts">
import HelloWorld from '@/components/HelloWorld.vue'
// 导入 useCounterStore 函数
import { useCounterStore } from '@/stores/counter'
// 创建 counterStore 实例
const counterStore = useCounterStore()
</script>
<template>
<div class="father">
<p>这是父组件</p>
<p>count: {{ counterStore.count }}</p>
<p>double: {{ counterStore.double }}</p>
<button @click="counterStore.increment">count++</button>
</div>
<HelloWorld/>
</template>
<style lang="scss" scoped>
</style>
<style lang="scss" scoped>
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
background-color: $bg-color;
}
</style>
子组件
<script setup lang="ts">
// 导入 useCounterStore 函数
import { useCounterStore } from '@/stores/counter'
// 创建 counterStore 实例
const counterStore = useCounterStore()
</script>
<template>
<div class="son">
<p>这是子组件</p>
<p>count: {{ counterStore.count }}</p>
<p>double: {{ counterStore.double }}</p>
<button @click="counterStore.subtraction">count--</button>
</div>
</template>
<style lang="scss" scoped>
.son{
height: 500px;
width: 500px;
border: 1px red solid;
}
</style>
点击父组件 count ++ 按钮页面变化
点击子组件 count -- 按钮页面变化
四、持久化 store 的状态(以@usevue/core举例)
下载 @usevue/core
npm i @vueuse/core
使用 useStorage 函数 定义持久化存储的数据
修改 store/couter.ts 代码
// src/store/counter.ts
import { defineStore } from "pinia";
// 导入 useStorage 函数
import { useStorage } from '@vueuse/core'
export const useCounterStore = defineStore("counter", () => {
// ref变量 → state 属性
// 使用 useStorage(存储名称, 初始值, 存储方式)
// 存储方式不给默认是localStorage
const count = useStorage('count', 0);
// computed计算属性 → getters
const double = computed(() => {
return count.value * 2;
});
// function函数 → actions
function increment() {
count.value++;
};
function subtraction() {
count.value--;
};
return { count, double, increment, subtraction };
});
重新运行代码
可以看到 localstorage 存储 count 数据
为 store/couter.ts 代码 useStorage 函数添加第三个参数 sessionStorage、并重新运行代码
const count = useStorage('count', 0, sessionStorage);
五、注意事项
1、直接通过对象建构获取的状态不是响应式的
官网代码: 定义 Store | Pinia
<script setup>
import { useCounterStore } from '@/stores/counter'
const store = useCounterStore()
// ❌ 这将不起作用,因为它破坏了响应性
// 这就和直接解构 `props` 一样
const { name, doubleCount } = store
name // 将始终是 "Eduardo"
doubleCount // 将始终是 0
setTimeout(() => {
store.increment()
}, 1000)
// ✅ 这样写是响应式的
// 💡 当然你也可以直接使用 `store.doubleCount`
const doubleValue = computed(() => store.doubleCount)
</script>
2、确保 pinia 实例被激活、才能使用 store
官网代码: 在组件外使用 store | Pinia
import { useUserStore } from '@/stores/user'
import { createApp } from 'vue'
import App from './App.vue'
// ❌ 失败,因为它是在创建 pinia 之前被调用的
const userStore = useUserStore()
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
// ✅ 成功,因为 pinia 实例现在激活了
const userStore = useUserStore()
import { createRouter } from 'vue-router'
const router = createRouter({
// ...
})
// ❌ 由于引入顺序的问题,这将失败
const store = useStore()
router.beforeEach((to, from, next) => {
// 我们想要在这里使用 store
if (store.isLoggedIn) next()
else next('/login')
})
router.beforeEach((to) => {
// ✅ 这样做是可行的,因为路由器是在其被安装之后开始导航的,
// 而此时 Pinia 也已经被安装。
const store = useStore()
if (to.meta.requiresAuth && !store.isLoggedIn) return '/login'
})