大菠萝(Pinia)符合真香定律
点击关注公众号,“技术干货”及时达!
前言最近在开发Vue3项目时,遇到了多层跨域数据传输的问题。起初,我用props和emit实现了爷孙组件之间的数据传递,效果不错。子组件通过defineEmit定义方法,父组件通过emit调用这些方法,而defineProps则负责接收参数,一切井然有序。
但问题出现在兄弟组件之间的数据传递上。兄弟组件的数据由父组件管理,两者不会直接修改数据,每次使用都从父组件独立获取属性或方法。然而,这些兄弟组件并非“堂兄弟”,而是“表兄弟”,数据传输变得复杂。
有人可能会想到用inject和provide实现跨层级传递,但随着项目规模扩大,数据来源不明确,维护起来会很麻烦。
这时,Vuex似乎是个解决方案,但今天的主角是更轻量级的Pinia,也就是“大菠萝”??
大菠萝Pinia是啥??Pinia 是 Vue 的存储库,它允许您跨组件/页面共享状态。
?这是Pinia官网对它的定义。Vuex用起来还是有点麻烦,React里的Context倒是还不错。而Pinia不仅能在Vue3中使用,还兼容Vue2,简直是开发者的福音。下面是 Pinia官网对自身的评价,看起来它对自己的定位非常清晰。
Pinia 常规操作
安装通过常用的包管理器进行安装:
yarn add pinia// 或者npm install pinia安装完成后,我们需要将Pinia挂载到Vue应用中。也就是说,我们需要创建一个根存储并传递给应用。我们需要修改main.js,引入Pinia提供的createPinia方法:
import { createApp } from 'vue'import App from './App.vue'import { createPinia } from 'pinia'
const pinia = createPinia()const app = createApp(App)app.use(pinia)app.mount('#app')创建Pinia实例,并在根组件中use一下,这样整个应用就可以使用Pinia来管理状态了。
StoreStore是Pinia的核心概念,它是一个中央数据仓库,专门用于状态管理。这里存放的数据是整个应用可以访问的全局数据,而组件内部的数据不应该放入Store。
使用Pinia提供的defineStore方法创建一个全局仓库。defineStore接收两个参数:
name:一个字符串,必传项,该Store的唯一ID。options:一个对象,Store的配置项,比如配置Store内的数据、修改数据的方法等等。defineStore配置store属性时有两种写法,一种是Option对象形式,一种是Setup函数形式。
对象形式(Option store)Store有三个核心概念:「state」、「getters」和「actions」,我们可以将它们类比为组件中的“「数据」”、“「计算属性」”和“「方法」”。(直观,推荐使用)
// 中央管理store import { defineStore } from 'pinia'import { ref } from 'vue'
// 第一个参数:唯一ID;第二个:一个对象,其他配置项export const useCounterStore = defineStore('counter', { // 推荐使用完整类型推断的箭头函数 state: () = { return { count: ref(0), isLogin: ref(false), } }, actions: { add() { this.count++ } }})函数形式(setup store)与Vue组合式API的setup函数 相似,我们可以传入一个函数,该函数定义了一些响应式属性和方法,并且返回一个带有我们想暴露出去的属性和方法的对象。
在Setup Store中:
ref()就是state属性computed()就是gettersfunction()就是actions可见,函数式相比对象式更灵活,还可以创建监听器
import { defineStore } from 'pinia';import { ref } from 'vue';
export const useCounterStore = defineStore('counter', () = { // 定义状态 const count = ref(0); const isLogin = ref(false);
// 定义 actions function add() { count.value++; }
// 返回状态和 actions return { count, isLogin, add, };});使用Store下面都说Option对象形式创建的store
可以在任意组件中引入定义的Store来进行使用。比如在App.js中:
template div 数量: {{ counterStore.count }} 是否登入: {{ counterStore.isLogin }} button @click="counterStore.add"增加Add/button /div/template
script setupimport { useCounterStore } from './stores/counter';
const counterStore = useCounterStore();/script实例化后,就可以使用Store里面的state、getters和actions中定义的任何属性了。
可能有人会说每次都用实例counterStore.去点方法/属性,有点麻烦。这时候大菠萝pinia又给舔了一嘴——使用「解构」直接
解构与直接访问一般来说,我们实例化Store后,直接进行读取和写入操作即可。
// CompA.jstemplate div {{ count }} button @click="add"B点我{{ count }}/button button @click="counterStore.add"A点我{{ counterStore.count }}/button /div/template
scriptimport { useCounterStore } from '../store/counter'const counterStore = useCounterStore()const { count, add } = useCounterStore();/script这里有个问题,如果直接解构,会发现数据失去了响应式。
这时候,我们可以使用toRef一个一个解构,或者使用toRefs一次性解构所有属性或方法。Pinia还有一个自带的解构状态storeToRefs,但是不包括方法。如果你使用的是 Vue 3 的script setup语法,可以直接解构和使用 Pinia store
// 使用 toRefs 一次性解构所有属性const { count, add } = toRefs(counterStore);
// 使用 toRef 逐个解构const count = toRef(counterStore, 'count');const add = toRef(counterStore, 'add');
// 使用 storeToRefs 解构状态const { count } = storeToRefs(counterStore);
// setup语法糖下,直接解构方法const { add } = counterStore;
State其他常用的操作还有:$reset、$patch、$state、$subscribe等,这些方法可以帮助我们更好地管理状态。
$reset重置为初始值import { useCounterStore } from './store/counter';const counterStore = useCounterStore();
counterStore.count = 10;// reset重置状态为初始值counterStore.$reset(); // count 恢复为初始值 02. 「$patch:批量更新状态」$patch可以一次性更新多个状态,支持对象或函数形式。
// 对象形式counterStore.$patch({ count: 5, isLogin: true,});
// 函数形式counterStore.$patch((state) = { state.count += 1; state.isLogin = false;});3. 「$state:替换整个状态」$state用于替换整个state对象。
counterStore.$state = { count: 100, isLogin: true,};4. 「$subscribe:监听状态变化」$subscribe可以监听state的变化,适合做一些副作用操作。
counterStore.$subscribe((mutation, state) = { console.log('状态变化了:', state.count);});
// 修改状态counterStore.count = 20; // 控制台输出: "状态变化了: 20"Actions直接通过this访问state、getters和其他action,不需要像vuex每次都去使用context。同时可以直接修改状态,不需要像vuex通过mutation。
Pinia 的action天然支持异步操作,比如async/await或Promise,无需额外配置。
actions: { async fetchData() { const data = await api.getData(); this.data = data; // 直接修改状态 }}除了异步,直接修改状态,调用其他action和访问getters这4项操作外,Pinia 的action还有以下能力:
「组合操作」:可以在一个action中执行多个操作,比如先调用 API,再修改状态,最后调用其他action。actions: { async login(credentials) { const user = await api.login(credentials); // 异步操作 this.user = user; // 直接修改状态 this.setLoginStatus(true); // 调用其他 action }, setLoginStatus(status) { this.isLoggedIn = status; // 直接修改状态 }}「数据持久化」store中的数据,刷新页面后就丢失了,如果想保留这些数据,就要用到数据持久化了。
推荐使用 「pinia-plugin-persistedstate」
「安装插件」yarn add pinia-plugin-persistedstate「将插件添加到pinia」import { createPinia } from 'pinia'import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()pinia.use(piniaPluginPersistedstate)小结?Pinia 的设计让状态管理更加直观和灵活,特别适合现代 Vue 开发!还是不得不说:Vuex太重,Pinia(大菠萝)轻巧好用,真香!
?点击关注公众号,“技术干货”及时达!
阅读原文
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。 项目经理在线