信任在座各位如果使用Vue生态开辟项目环境下,对Pinia状态管理库应该有所听闻或正在使用,如果还没打仗到Pinia,这篇文章可以帮你快速入门,并如安在企业项目中更优雅封装使用。
本文先给各人论述怎样去明白、使用Pinia,末了讲怎样把Pinia集成到工程中,得当大多数读者,至于研读Pinia的源码等进阶科普,会别的开一篇文章细述。别的,本文的所有demo,都专门开了个GitHub项目来生存,有必要的同学可以拿下来实操一下。??
熟悉Pinia
Pinia读音:/piːnjʌ/,是Vue官方团队保举代替Vuex的一款轻量级状态管理库。 它最初的计划理念是让Vue Store拥有一款Composition API方式的状态管理库,并同时能支持 Vue2.x版本的Option API 和 Vue3版本的setup Composition API开辟模式,并完整兼容Typescript写法(这也是优于Vuex的告急因素之一),实用于所有的vue项目。
比起Vuex,Pinia具备以下长处:
- 完整的 TypeScript 支持:与在 Vuex 中添加 TypeScript 相比,添加 TypeScript 更轻易
- 极其轻便(体积约 1KB)
- store 的 action 被调理为常规的函数调用,而不是使用 dispatch 方法或 MapAction 辅助函数,这在 Vuex 中很常见
- 支持多个Store
- 支持 Vue devtools、SSR 和 webpack 代码拆分
Pinia与Vuex代码分割机制
上述的Pinia轻量有一部分体现在它的代码分割机制中。
举个例子:某项目有3个store「user、job、pay」,别的有2个路由页面「首页、个人中心页」,首页用到job store,个人中心页用到了user store,分别用Pinia和Vuex对其状态管理。
先看Vuex的代码分割: 打包时,vuex会把3个store归并打包,当首页用到Vuex时,这个包会引入到首页一起打包,末了输出1个js chunk。如许的题目是,实在首页只必要此中1个store,但其他2个无关的store也被打包进来,造成资源浪费。
Pinia的代码分割: 打包时,Pinia会查抄引用依靠,当首页用到job store,打包只会把用到的store和页面归并输出1个js chunk,其他2个store不耦合在此中。Pinia能做到这点,是由于它的计划就是store分离的,办理了项目标耦合题目。
Pinia的常规用法
事不宜迟,直接开始使用Pinia「本文默认使用Vue3的setup Composition API开辟模式」。
如果你对Pinia使用熟悉,可以略过这part?
1. 安装
yarn add pinia# or with npmnpm install pinia复制代码2. 挂载全局实例
import { createPinia } from 'pinia'app.use(createPinia())复制代码3. 创建第一个store
在src/store/counterForOptions.ts创建你的store。界说store模式有2种:
- 使用options API模式界说,这种方式和vue2的组件模子形式雷同,也是对vue2技能栈开辟者较为友爱的编程模式。
import { defineStore } from 'pinia';// 使用options API模式界说export const useCounterStoreForOption = defineStore('counterForOptions', { // 界说state state: () => { return { count1: 1 }; }, // 界说action actions: { increment() { this.count1++; } }, // 界说getters getters: { doubleCount(state) { return state.count1 * 2; } }});复制代码
- 使用setup模式界说,符合Vue3 setup的编程模式,让结构更加扁平化,个人保举保举使用这种方式。
import { ref } from 'vue';import { defineStore } from 'pinia';// 使用setup模式界说export const useCounterStoreForSetup = defineStore('counterForSetup', () => { const count = ref<number>(1); function increment() { count.value++; } function doubleCount() { return count.value * 2; } return { count, increment, doubleCount };});复制代码
上面2种界说方式结果都是一样的,我们用defineStore方法界说一个store,内里分别界说1个count的state,1个increment action 和1个doubleCount的getters。此中state是要共享的全局状态,而action则是让业务方调用来改变state的入口,getters是获取state的盘算结果。
之以是用第一种方式界说是还要额外写getters、action关键字来区分,是由于在options API模式下可以通过mapState()、mapActions()等方法获取对应项;但第二种方式就可以直接获取了(下面会细述)。
4. 业务组件对store的调用
在src/components/PiniaBasicSetup.vue目次下创建个组件。
<script setup lang="ts" name="component-PiniaBasicSetup">import { storeToRefs } from 'pinia';import { useCounterStoreForSetup } from '@/store/counterForSetup';// setup composition API模式const counterStoreForSetup = useCounterStoreForSetup();const { count } = storeToRefs(counterStoreForSetup);const { increment, doubleCount } = counterStoreForSetup;</script><template> <div这里。</p></blockquote>5. 精良的编程风俗
state的改变交给action行止理: 上面例子,counterStoreForSetup有个pinia实例属性叫$state是可以直接改变state的值,但不发起怎么做。一是难维护,在组件繁多环境下,一处潜伏state更改,整个开辟组帮你排查;二是粉碎store封装,难以移植到其他地方。以是,为了你的荣誉和安全着想,请克制游离之外的coding??。
用hook代替pinia实例属性: install后的counterStoreForSetup对象内里,带有不少$开头的方法,实在这些方法大多数都能通过hook引入代替。
其他的想到再增补...
企业项目封装攻略
1. 全局注册机
重复打包题目
在上面的例子我们可以知道,使用store时要先把store的界说import进来,再实行界说函数使得实例化。但是,在项目渐渐巨大起来后,每个组件要使用时间都要实例化吗?在文中开头讲过,pinia的代码分割机制是把引用它的页面归并打包,那像下面的例子就会有题目,user被多个页面引用,末了user store被重复打包。
为了办理这个题目,我们可以引入 ”全局注册“ 的概念。做法如下:
创建总入口
在src/store目次下创建一个入口index.ts,此中包罗一个注册函数registerStore(),其作用是把整个项目标store都提前注册好,末了把所有的store实例挂到appStore透传出去。如许以后,只要我们在项目任何组件要使用pinia时,只要import appStore进来,取对应的store实例就行。
// src/store/index.tsimport { roleStore } from './roleStore';import { useCounterStoreForSetup } from '@/store/counterForSetup';import { useCounterStoreForOption } from '@/store/counterForOptions';export interface IAppStore { roleStore: ReturnType<typeof roleStore>; useCounterStoreForSetup: ReturnType<typeof useCounterStoreForSetup>; useCounterStoreForOption: ReturnType<typeof useCounterStoreForOption>;}const appStore: IAppStore = {} as IAppStore;/** * 注册app状态库 */export const registerStore = () => { appStore.roleStore = roleStore(); appStore.useCounterStoreForSetup = useCounterStoreForSetup(); appStore.useCounterStoreForOption = useCounterStoreForOption();};export default appStore;复制代码
总线注册
在src/main.ts项目总线实行注册利用:
import { createApp } from 'vue';import App from './App.vue';import { createPinia } from 'pinia';import { registerStore } from '@/store';const app = createApp(App);app.use(createPinia());// 注册pinia状态管理库registerStore();app.mount('#app');复制代码
业务组件内直接使用
// src/components/PiniaBasicSetup.vue<script setup lang="ts" name="component-PiniaBasicSetup">import { storeToRefs } from 'pinia';import appStore from '@/store';// setup composition API模式const { count } = storeToRefs(appStore.useCounterStoreForSetup);const { increment, doubleCount } = appStore.useCounterStoreForSetup;</script><template> <div2. Store组管理
场景分析
各人在项目中是否经常碰到某个方法要更新多个store的环境呢?比方:你要做个游戏,有3种职业「兵士、法师、羽士」,别的,玩家脚色有3个store来控制「人物属性、装备、技能」,页面有个”转职“按钮,可以转其他职业。当玩家改变职业时,3个store的state都要改变,怎么做呢?
- 方法1:在业务组件创建个函数,单点击”转职“时,获取3个store并且更新它们的值。
- 方法2:抽象一个新pinia store,store里有个”转职“的action,当玩家转职时,相应这个action,在action更新3个store的值。
对比起来,无论从封装还是业务解耦,显着方法2更好。要做到如许,这也得益于pinia的store独立管理特性,我们只必要把抽象的store作为父store,「人物属性、装备、技能」3个store作为单位store,让父store的action去管理本身的单位store。
组级Store创建
继承上才艺,父store:src/store/roleStore/index.ts
import { defineStore } from 'pinia';import { roleBasic } from './basic';import { roleEquipment } from './equipment';import { roleSkill } from './skill';import { ROLE_INIT_INFO } from './constants';type TProfession = 'warrior' | 'mage' | 'warlock';// 脚色组,汇聚「人物属性、装备、技能」3个store统一管理export const roleStore = defineStore('roleStore', () => { // 注册组内store const basic = roleBasic(); const equipment = roleEquipment(); const skill = roleSkill(); // 转职业 function changeProfession(profession: TProfession) { basic.setItem(ROLE_INIT_INFO[profession].basic); equipment.setItem(ROLE_INIT_INFO[profession].equipment); skill.setItem(ROLE_INIT_INFO[profession].skill); } return { basic, equipment, skill, changeProfession };});复制代码单位Store
3个单位store:
业务组件调用
<script setup lang="ts" name="component-StoreGroup">import appStore from '@/store';</script><template> <div落幕
磨刀不误砍柴工,对于一个项目来讲,好的状态管理方案在当中发挥告急的作用,不但能让项目思绪清晰,而且便于项目日后维护和迭代。 |