Pinia 介绍
Pinia 是一个专门为 Vue 设计的状态管理库,它提供了一种简单和直观的方式来管理应用程序的状态.
在使用 Pinia 之前,可以轻松的创建定义状态的存储,然后将其与 Vue 组件绑定,使他们能够使用该状态.
Pinia 更加简单易用,体积更小,同时具有更好的 TS 支持和插件系统
安装和配置 Pinia
安装和配置 Pinia 非常简单,像其他 Vue 插件一样,Pinia 需要通过 yarn 和 npm 进行安装 并且与 Vue 应用程序进行绑定
安装命令
yarn add pinia
npm install pinia
在安装完 Pinia 包之后,需要在 main.js 文件中导入 createPinia 函数并将 Pinia 插件与 Vue 应用程序绑定:如下
import { createApp } from "vue";
import { createPinia } from "pinia";
import App from "./App.vue";
const app = createApp(App);
const pinia = createPinia();
app.use(pinia);
app.mount("#app");
使用 createPinia() 函数创建并初始化 Pinia 插件实例,将其与 Vue 应用程序绑定使用 app.use(pinia)。至此,我们就可以使用 Pinia 来管理 Vue 应用程序的状态了。
Pinia 的核心
Store
Store 是 Pinia 中管理状态的核心理念.它相当于一个 Vue 组件中的状态,但是 Store 是一个独立的模块
Store 是用 defineStore()来定义的,它的第一个参数要求是一个独一无二的的名字, 这个名字也被用作 id,是必须传入的.
Pinia 将用它来链接 store 和 devtools 为了养成习惯性的用法,将返回函数命名为 use...是一个符合组合式函数风格的约定
defineStore()的第二个参数可接受两类值:Setup 函数或者 Option 对象
定义 Store 的示例代码
import { defineStore } from "pinia";
// 你可以对 `defineStore()` 的返回值进行任意命名,但最好使用 store 的名字,同时以 `use` 开头且以 `Store` 结尾。(比如 `useUserStore`,`useCartStore`,`useProductStore`)
// 第一个参数是你的应用中 Store 的唯一 ID。
export const useAlertsStore = defineStore("alerts", {
// 其他配置...
});
State
State 是 store 中存储数据的地方.通过定义 State,可以在 Store 任意位置访问和修改数据
在 Pinia 中,state 被定义为一个返回初始状态的函数,这使的 Pinia 可以同时支持服务器端和客户端
定义 State 的示例代码如下:
import { defineStore } from "pinia";
const useStore = defineStore("storeId", {
// 为了完整类型推理,推荐使用箭头函数
state: () => {
return {
// 所有这些属性都将自动推断出它们的类型
count: 0,
name: "Eduardo",
isAdmin: true,
items: [],
hasChanged: true,
};
},
});
Getter
Getter 用来获取从 State 派生的数据,类似于 Vue 组件中的 computed 计算属性.可以通过 defineStore()中的 getters 属性来定义它们
推荐使用箭头函数,并且它将接收 state 作为第一个参数
箭头函数语法()=>({}) 一定要加括号,表示return括号里面的对象
import { defineStore } from "pinia";
export const useStore = defineStore('main', {
state:()=>({
count:0
})
getters: {
doubleCount: (state) => state.count * 2,
},
})
Action
Action 相当于组件中的方法,它们可以通过 defineStore()中的 actions 属性来定义,Action 是一种异步操作封装在 Store 中的方式
它可以是一个可以被调用的函数.也可以接收参数并修改 store 中的状态.Action 应该始终是同步的,并返回一个 Promise 对象
以便处理异步操作的时候能够很好处理结果
Pinia 中的 Action 是由 defineStore 创建,可以通过在 actions 中定义它们来使用它们。例如下面是一个 store 中的 Action 定义
import { defineStore } from "pinia";
export const myStore = defineStore("mystore", {
state: () => ({ message: "Hello", count: 0 }),
getters: {
doublecount: () => {
return state.count++;
},
},
actions: {
async fetchmessage() {
const response = await fetch("https://xxxx.com/php");
const data = await response.json();
this.message = data.message;
},
},
});
在上面的示例中,我们为 myStore 定义了一个 Action , fetchMessage() ,它会从后台 API 中获取数据,并更新 store 中的状态。然后,我们可以从组件或其他 Action 中调用该 Action :
<template>
<div>
<button @click="changestate">点击开始</button>
</div>
</template>
<script setup>
import { useLoginStore } from "@/stores/login.js";
import { useCounterStore } from "@/stores/counter.js";
const login = useLoginStore();
const counter = useCounterStore();
const changestate = () => {
console.log("点击了");
login.changeLoginflag(true);
console.log(counter.count);
};
</script>
<style lang="scss" scoped></style>
在上面的代码中,我们在组件中使用 useStore 钩子来获取 store 实例,然后将其传递给 fetchMessage() 方法。该方法将从应用程序的后台获取数据,并更新存储器中的状态。最后,公开了一个 handleClick() 方法,以便组件可以调用它并触发 Action 。
创建和使用 Pinia
创建 Pinia
前面我们已经安装和配置好了 Pinia,在创建 Pinia 之前,为了代码的统一管理和可维护性,我们依然先创建一个 store 文件夹,然后在来创建相关的 Pinia,具体步骤如下
在 src 文件夹下新建 store 文件夹,后面所有涉及需要 Pinia 进行状态管理的代码都放在该文件夹下
在 store 文件夹下新建 movieListStore.js 文件,创建完成后,打开该文件
在 movieListStore.js 文件中引入 Pinia 中的 defineStore 方法
import { defineStore } from "pinia";
- 创建 defineStore 对象,定义一个 useMovieListStore 用于接收 defineStore 创建的对象,并将其通过 export default 导出
import { ref, computed } from "vue";
import { defineStore } from "pinia";
export const useCounterStore = defineStore("counter", () => {
const count = ref(0);
const doubleCount = computed(() => count.value * 2);
function increment() {
count.value++;
}
return { count, doubleCount, increment };
});
在上面的代码中,我们使用 action 定义了两个方法,
一个同步方法 setIsShow,
一个异步方法 fetchMovies
注意:
这里需要注意,官方建议我们在定义钩子函数时,建议使用 use 开头 Store 结尾的命名方式来对上面创建的对象进行命名,如上面的 useMovieListStore
使用 Pinia
前面我们已经创建好了 Pinia,接下来,我们就可以在组件中使用了。
在 Vue 组件中使用 store,我们需要通过 useStore() 函数访问 store 的实例。
在 Vue 组件中使用 Pinia 的步骤如下:
- 先使用 import 引入 模块
import { useLoginStore } from "@/stores/login.js";
- 创建 useStore 对象
const login = useLoginStore();
- 在需要获取状态的地方通过上面定义的 store.getIsShow()获取状态
return {
isShow: login.Loginflag,
};
Menu.vue 中完整的示例如下:
<template>
<div>
<button @click="changestate">点击开始</button>
</div>
</template>
<script setup>
import { useLoginStore } from "@/stores/login.js";
import { useCounterStore } from "@/stores/counter.js";
const login = useLoginStore();
const counter = useCounterStore();
const changestate = () => {
console.log("点击了");
login.changeLoginflag(true);
console.log(counter.count);
};
</script>
<style lang="scss" scoped></style>
Pinia 的 Option Store 方式定义 Store
Option Store 方式定义 Store 与 Vue 的选项式 API 类似,我们通过传入一个带有 state、actions 与 getters 属性的 Option 对象来定义,示例代码如下:
export const useCounterStore = defineStore("counter", {
state: () => ({ count: 0 }),
getters: {
double: (state) => state.count * 2,
},
actions: {
increment() {
this.count++;
},
},
});
我们可以认为 state 是 store 的数据 (data),getters 是 store 的计算属性 (computed),而 actions 则是方法 (methods)。
Pinia 的 Setup Store 方式定义 Store
Setup Store 与 Option Store 稍有不同,它与 Vue 组合式 API 的 setup 函数 相似,我们通过传入一个函数,该函数定义了一些响应式属性和方法,并且返回一个带有我们想暴露出去的属性和方法的对象。示例代码如下:
export const useCounterStore = defineStore("counter", () => {
const count = ref(0);
function increment() {
count.value++;
}
return { count, increment };
});
在 Setup Store 中:
ref() 就是 state 属性
computed() 就是 getters
function() 就是 actions
示例代码
下面通过一个实例来完整的说明 Pinia 状态管理的使用方法,现在要实现如下效果:
现在页面上需要完成两个功能,一个功能是通过监听 isShow 的值,来控制不同页面跳转时,底部菜单栏 button 的显示和隐藏;另一个功能是通过一个函数连接网络获取电影列表,并在详情页展示出来
在选项式 API 中,实现代码如下:
// main.js
import "./assets/main.css";
import { createApp } from "vue";
import { createPinia } from "pinia";
import App from "./App.vue";
import router from "./router";
const app = createApp(App);
app.use(createPinia());
app.use(router);
app.mount("#app");
- store 文件夹下 login.js 中的代码
这里面也包含了模块直接的通信
import { ref } from "vue";
import { defineStore, storeToRefs } from "pinia";
import { useCounterStore } from "./counter";
export const useLoginStore = defineStore("login", () => {
//counter模块
const counterStore = useCounterStore();
//解构模块
const { count } = storeToRefs(counterStore);
//counter模块
let Loginflag = ref(false);
function changeLoginflag(content) {
console.log(content);
Loginflag.value = content;
count.value = 5;
}
return { changeLoginflag, Loginflag };
});
- store 文件夹下 counter.js 中的代码
import { ref, computed } from "vue";
import { defineStore } from "pinia";
export const useCounterStore = defineStore("counter", () => {
const count = ref(0);
const doubleCount = computed(() => count.value * 2);
function increment() {
count.value++;
}
return { count, doubleCount, increment };
});
- components 文件夹下 Menu.vue 文件的代码
<template>
<div>
<button @click="changestate">点击开始</button>
</div>
</template>
<script setup>
import { useLoginStore } from "@/stores/login.js";
import { useCounterStore } from "@/stores/counter.js";
const login = useLoginStore();
const counter = useCounterStore();
const changestate = () => {
console.log("点击了");
login.changeLoginflag(true);
console.log(counter.count);
};
</script>
<style lang="scss" scoped></style>