1:Pinia 西班牙语:菠萝
1.1:Pinia是什么?
Pinia 是Vue 官方新一代状态统一管理库,当数据需要在多个不相关的组件间共享的时候,需要使用Pinia,类似 Redis 集中缓存。专门为vue2和vue3设计,是Vuex 的替代方案。Vue 3 官方已推荐新项目直接使用 Pinia,不再主推 Vuex。
我们在上一章节的路由器中学习了组件之间传递数据,比如多层嵌套、兄弟组件、跨页面路由之间,数据传递会非常麻烦。很麻烦,但是pinia可以完美的结局这个问题
👉 Pinia:把公共数据放到全局仓库 (store),任意组件都可以直接读取、修改。
F5 刷新页面:Pinia 清空,前端重新请求接口,再回填 Pinia。
补充:如果想让 Pinia 拥有类似 redis “持久化” 效果,前端可以搭配pinia‑plugin‑persistedstate,把数据存到浏览器localStorage。localStorage 相当于浏览器本地小磁盘,但依然只属于这台电脑这个浏览器,别的设备访问不到,和后端 Redis 不是一回事。
1.2:有什么作用
管理跨组件共享全局状态
场景举例(工业后台 / MES 系统经常用到):
- 用户登录信息、token、角色权限、菜单列表
- 页面全局配置、筛选条件缓存
- 多个页面共享的设备列表、工单筛选参数
- 弹窗全局状态、消息通知
如果不用状态库:组件之间多层传参(props 层层传递),代码耦合严重; 使用 Pinia:任意组件直接读取 / 修改全局状态
1.3:Pinia 对比 Vuex 核心优势(面试必说)
移除了 MutationVuex:修改 state 必须走 mutation(同步)、action 处理异步,模板代码繁琐 Pinia:可以直接修改 state;异步逻辑写在 action 里,结构简单。
没有嵌套 modulesVuex 模块嵌套store.user.token; Pinia 每个 store 是独立文件,扁平结构,导入即用,不用命名空间 namespaced。
原生 TypeScript 支持极好类型自动推导,不需要额外复杂类型声明,非常适合<script setup>+ TS 项目。
代码更简洁,样板代码极少不需要commit、dispatch,降低学习成本。
支持组件外部调用 store路由守卫、axios 拦截器里可以直接引入 store 操作状态(Vuex 也能实现,但 Pinia 更顺畅)。
Devtools 调试友好,支持时间旅行调试。
1.4:Pinia的核心状态
Pinia 三大核心概念
- state:状态,存放共享数据(等同于 Vuex 的 state)
- gettters:派生状态,相当于计算属性,有缓存
- actions:修改状态方法,支持同步、异步(替代 Vuex mutation + action)
2:实战开始
2.1:使用pinia之前,组件count.vue
<template> <div> <h1>pinia的person组件</h1> <h2>计数器:{{ count }}</h2> 名字:<input type="text" v-model="name">{{ name }} <hr> <!-- v-model双向数据绑定。影响value属性 --> <select v-model.number="n"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <button @click="add">+数字</button> <button @click="minus">-数字</button> </div> </template> <script setup lang="ts"> import { ref } from 'vue'; let name = ref("张三"); console.log(name); // 计数器方法 let count = ref(0); let n = ref(1); function add() { count.value += n.value; } function minus() { count.value -= n.value; } </script> <style></style>2.2:使用pinia做状态集中管理
1:首先安装pinia
npm install pinia2:在main.ts中使用pinia
import { createApp } from 'vue' import { createPinia } from 'pinia' import App from './App.vue' const app = createApp(App) //创建pinia const pinia = createPinia() //使用pinia app.use(pinia) app.mount('#app')2.3:新建store下的count.ts 操作pinia
import { defineStore } from "pinia"; //定义仓库 const userCountStore = defineStore("count", { //状态 state,存放共享数据(等同于 Vuex 的 state) state() { //返回数据 return { name:"张三", count: 0, n: 1, }; }, // Actions:修改状态的方法,支持同步/异步(替代Vuex mutation+action) actions: { add2():void { this.count += this.n; }, minus2() { this.count -= this.n; }, // 异步示例 async changeName(newName: string) { // 可以在这里请求后端接口 this.name = newName; }, }, }); //导出仓库 默认单利,多组件共享 //export default userCountStore; export { userCountStore };2.4:count.vue组件使用pinia
<template> <div> <h1>pinia的person组件</h1> <h2>计数器:{{ count }}</h2> 名字:<input type="text" v-model="name">{{ name }} <hr> <!-- v-model双向数据绑定。影响value属性 --> <select v-model.number="n"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <button @click="add">+数字</button> <button @click="minus">-数字</button> <hr> <h1>使用pinia</h1> <h2>计数器:{{ countStore.count }}</h2> <!-- v-model双向数据绑定。影响value属性 --> <select v-model.number="countStore.n"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <button @click="add1">+数字</button> <button @click="minus1">-数字</button> <hr> <button @click="add11">+数字</button> <button @click="minus11">-数字</button> <hr> </div> </template> <script setup lang="ts"> import { ref } from 'vue'; import { userCountStore } from '../store/count' let name = ref("张三"); console.log(name); //1:不实用pinia 计数器方法 let count = ref(0); let n = ref(1); function add() { count.value += n.value; } function minus() { count.value -= n.value; } //2:使用pinia, 得到单利的countStore let countStore = userCountStore() function add1() { countStore.count += countStore.n; } function minus1() { countStore.count -= countStore.n; } function add11() { //通过仓库调用方法 countStore.add2(); } function minus11() { //通过仓库调用方法 countStore.minus2(); } </script> <style></style>2.5:storeToRefs
storeToRefs是 Pinia 提供的工具函数,专门用来安全解构 Store 中的 state、getter,保证解构出来的变量依旧保留响应式。
<template> <div> <h1>pinia的person组件</h1> <h2>计数器:{{ count }}</h2> 名字:<input type="text" v-model="name">{{ name }} <hr> <!-- v-model双向数据绑定。影响value属性 <select v-model.number="n"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <button @click="add">+数字</button> <button @click="minus">-数字</button> --> <hr> <h1>使用pinia</h1> <h2>计数器:{{ count }}</h2> <!-- v-model双向数据绑定。影响value属性 --> <select v-model.number="n"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <button @click="add1">+数字</button> <button @click="minus1">-数字</button> <hr> <button @click="add11">+数字</button> <button @click="countStore.minus2">-数字</button> <hr> </div> </template> <script setup lang="ts"> import { ref } from 'vue'; import { userCountStore } from '../store/count' import { storeToRefs } from 'pinia'; let name = ref("张三"); console.log(name); // //1:不实用pinia 计数器方法 // let count = ref(0); // let n = ref(1); // function add() { // count.value += n.value; // } // function minus() { // count.value -= n.value; // } //2:使用pinia, 得到单利的countStore let countStore = userCountStore() //storeToRefs结构才能响应式 let {count,n}=storeToRefs(countStore); //这两个对象也有了响应式 function add1() { countStore.count += countStore.n; } function minus1() { countStore.count -= countStore.n; } function add11() { //通过仓库调用方法 countStore.add2(); } function minus11() { //通过仓库调用方法 countStore.minus2(); } </script> <style></style>2.6:pinia持久话,因为页面刷新数据会丢失
1:安装持久化插件
npm install pinia-plugin-persistedstate2:main.ts 注册插件
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' const pinia = createPinia() pinia.use(piniaPluginPersistedstate)3:仓库开启持久化:
export const usePersonStore = defineStore('person', { state: () => ({...}), getters:{}, actions:{}, persist: true // 开启持久化 })