状态管理库基本介绍
所谓状态管理库,就是用于管理一个应用中组件的状态的。
传统方式组件之间传递状态:
- 父传子用 Props
- 子传父用 Emit
这种方式存在的问题?
如果你的应用的规模一旦慢慢变大,那么不同层级之间组件的状态传递,就会变得非常的麻烦。
状态管理库如何解决这个问题的?
在状态管理库中,会有一个统一的地方(数据仓库)管理所有的状态,这个时候组件之间要进行状态的传递,只需要一个组件将状态提交到仓库,然后另一个组件从仓库获取最新的状态即可。
Vue生态的状态管理库
目前,Vue 生态官方所推荐的状态管理库是 Pinia,这是目前最新的状态管理库,用于替代以前的 Vuex 的,因此我们也是以 Pinia 为主,介绍这个最新的状态管理库。
Pinia ,发音为 /piːnjʌ/,来源于西班牙语 piña 。意思为菠萝,表示与菠萝一样,由很多小块组成。在 Pinia 中,每个 Store 都是单独存在,一同进行状态管理。
Pinia 是由 Vue.js 团队成员开发,最初是在 2019 年 11 月左右作为一项实验性工作提出的,目的是为了使用 Composition API 重新设计 Vuex,探索 Vuex 下一次迭代会是什么样子。但是 Pinia 在设计之初就倾向于同时支持 Vue 2 和 Vue 3,并且不强制要求开发者使用组合式 API。在探索的过程中,Pinia 实现了 Vuex5 提案的大部分内容,于是就直接取而代之了。
目前 Vue 官方已经宣布 Pinia 就是新一代的 Vuex,但是为了尊重作者本人,名字保持不变,仍然叫做 Pinia。
与之前的 Vuex 相比,Pinia 提供了更简单的 API,更少的规范,以及 Composition-API 风格的 API 。更重要的是,与 TypeScript 一起使用具有可靠的类型推断支持。
Pinia 官网地址:https://pinia.vuejs.org/
对比之前的 Vuex,Pinia 具有如下的特点:
- mutations 不复存在。只有 state 、getters 、actions
- actions 中支持同步和异步方法修改 state 状态
- 与 TypeScript 一起使用具有可靠的类型推断支持
- 不再有模块嵌套,只有 Store 的概念,Store 之间可以相互调用
- 支持插件扩展,可以非常方便实现本地存储等功能
- 更加轻量,压缩后体积只有 2kb 左右
快速入门
首先第一步仍然是安装
npm install pinia
接下来,需要在 Vue 应用中挂载 Pinia
import { createApp } from 'vue'
// 引入了根组件
import App from './App.vue'
import { createPinia } from 'pinia'
// 挂载根组件
const app = createApp(App)
// 创建一个 pinia 的实例
const pinia = createPinia()
app.use(pinia).mount('#app')
下一步就是创建数据仓库。src 目录下面创建一个 stores 是目录,该目录是数据仓库目录,下面可以对应多个数据仓库,每个数据仓库就是一个 JS 文件。
注意名字一般叫做 useXXXStore:
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
// 定义数据状态
state: () => {
return {
count: 0
}
},
// 定义了修改数据状态的两个方法
actions: {
increment() {
this.count++
},
decrement() {
this.count--
}
}
})
通过 defineStore 方法来创建一个数据仓库,该方法接收两个参数:
- 仓库名称
- 配置对象,在该配置对象里面就可以定义 state、getters、actions
最后就可以在组件中,使用数据仓库里面的状态:
<template>
<div class="counter">
<h1>计数器:{{ conterStore.count }}</h1>
<button @click="conterStore.increment">增加</button>
<button @click="conterStore.decrement">减少</button>
</div>
</template>
<script setup>
import { useCounterStore } from './stores/useCounterStore.js'
// 获取数据仓库
const conterStore = useCounterStore()
</script>
<style scoped>
.counter {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
}
button {
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
}
</style>