Pinia进阶:优雅的setup(函数式)写法+封装

程序员 2024-9-16 01:10:46 122 0 来自 中国
信任在座各位如果使用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也被打包进来,造成资源浪费。
2.png 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落幕

磨刀不误砍柴工,对于一个项目来讲,好的状态管理方案在当中发挥告急的作用,不但能让项目思绪清晰,而且便于项目日后维护和迭代。
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-11-22 01:54, Processed in 0.229760 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表