关于 Vue3 Store 的两种写法

2,616次阅读
没有评论

共计 3109 个字符,预计需要花费 8 分钟才能阅读完成。

前言

因为 Vue3 的组合式 API 的出现,结合我自己的一些使用,分享一下我的个人看法;因为有了组合式 API 的出现,让代码的组装更灵活了,数据管理可拆分,所以有了 class 写法和 pinia,我个人主要是 React + Vue 都写,公司用 React,个人的项目、作品用 Vue3,也做了一套 Vue3 的组件库 https://github.com/D-xuanmo/dl-ui

两种写法

目前 Vue 官网推荐的 store 解决方案是 pinia,用于替代 Vuex,我在开发过程中,部分场景会考虑使用 class 方式去管理数据,得益于 Vue3 响应式 API 可以脱离组件使用,只有业务型项目才会引入 pinia,这个仅我的观点,欢迎大家讨论;

  • 当一个独立的组件,功能比较复杂时,我会优先考虑 class 来进行数据层管理,class 也可以更好的提供 TS 类型推导,提供给外部使用;
  • 如果是一个业务系统类,我会考虑引入 pinia 进行数据管理,因为页面数据需要共享、数据持久化等;

class 写法

关于 reactiveref 的使用,我目前的用法是复杂数据类型我更愿意使用 reactive 来进行存储,因为在 Vue 模板之外不需要使用 .value 语法,简易数据类型则使用 ref 进行存储,因为 reactive 不支持

拿我组件库 FormStore 写法举例,源码地址 https://github.com/D-xuanmo/dl-ui/blob/develop/packages/dl-common/src/form/store/index.ts,部分写法如下:

class Store {
  /**
   * 表单数据模型,使用 reactive 管理一个复杂的对象
   */
  private models: UnwrapNestedRefs> = reactive(new Map())

  /**
   * 表单禁用,使用 ref 管理一个独立的小数据
   */
  public formDisabled = ref(false)

  /**
   * 显示属性联动 store,对于 store 也需要进行功能拆分,各自管理自己的功能,在通过主 store 进行对外暴露
   */
  public viewLinkageStore = new ViewLinkageStore(this)

  /**
   * 明细表数据
   */
  public detailTableStore = new DetailTableStore(this)

  /**
   * 事件中心
   */
  public events = new EventEmitterEx()}

封装组合式 API

因为一个大的组件,一般都有数据共享的场景,此时可以通过 provide 结合封装组合式 API 来提供方法暴露,比如我表单场景下的 events 事件中心需要共享,这个涉及到另一个概念,此处咱不做说明,有点偏题

events 一般在监听后,组件销毁时也需要注销,此时则可以提供一个组合式 API

import {useForm} from './use-form'
import {EventsType, IFormModelItem, IRenderModel} from '../types'
import {EventPrefixEnum} from '../constants'
import {onUnmounted} from 'vue'

/**
 * 定义一个用于触发事件的函数类型。* @param eventName - 事件名称
 * @param value - 事件的值
 * @param rowId - 行 ID(可选)* @typeparam T - 事件回调函数的参数类型,默认为 unknown
 */
type EmitType = (eventName: string, value?: T, rowId?: string) => void

/**
 * 根据给定的模型对象注册事件监听器,并返回一个用于触发事件的函数。* @param model - 表单模型对象或渲染模型对象
 * @returns 一个函数,用于触发事件;如果发生异常,则返回 undefined
 * @template T - 事件回调函数的参数类型,默认为 unknown
 */
const emit = (model: IFormModelItem | IRenderModel): EmitType | undefined => {
  try {const { store} = useForm()
    const id = (model as IFormModelItem).dataKey || model.id

    /**
     * 触发事件的函数。* @param eventName - 事件名称
     * @param value - 事件的值
     * @param rowId - 行 ID(可选)*/
    return (eventName, value, rowId) => {store.events.emit(`${EventPrefixEnum.FIELD}.${id}.${eventName}`, value, rowId)
    }
  } catch {return undefined}
}

/**
 * 注册事件监听器,并在组件卸载时自动取消注册。* @param eventName - 事件名称
 * @param callback - 事件回调函数
 */
const on = (eventName: EventsType, callback: (...params: any) => void) => {const { store} = useForm()

  // 注册事件的函数
  store.events.on(eventName, callback)

  // 在组件卸载时取消注册事件
  onUnmounted(() => {store.events.off(eventName, callback)
  })
}

/**
 * 定义一个 useFormEvent 函数,用于创建表单事件对象。* @param model - 表单模型对象或渲染模型对象
 * @returns 表单事件对象,包含 emit 和 on 方法
 * @template T - 事件回调函数的参数类型,默认为 unknown
 */
export const useFormEvent = (model: IFormModelItem | IRenderModel) => {
  return {
    on,
    emit: emit(model)
  }
}

pinia 大菠萝

pinia 则是全局类型的 store 共享数据,并非 class 可以做到单例,多实例共存,所以更适用于业务型项目,引用官网两段代码:

export const useCounterStore = defineStore('counter', {state: () => ({count: 0, name: 'Eduardo'}),
  getters: {doubleCount: (state) => state.count * 2,
  },
  actions: {increment() {this.count++},
  },
})

嵌套 store 的场景

import {useUserStore} from './user'

export const useCartStore = defineStore('cart', () => {const user = useUserStore()
  const list = ref([])

  const summary = computed(() => {return `Hi ${user.name}, you have ${list.value.length} items in your cart. It costs ${price.value}.`
  })

  function purchase() {return apiPurchase(user.id, this.list)
  }

  return {summary, purchase}
})

写在最后

两种 store 写法各有自己的业务场景,没有谁比谁好,选择自己合适方法进行开发则是最好的写法,欢迎大家讨论 关于 Vue3 Store 的两种写法

我的开源

文章来源: 关于 Vue3 Store 的两种写法

    正文完
     0
    Yojack
    版权声明:本篇文章由 Yojack 于2024-09-13发表,共计3109字。
    转载说明:
    1 本网站名称:优杰开发笔记
    2 本站永久网址:https://yojack.cn
    3 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
    4 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
    5 本站所有内容均可转载及分享, 但请注明出处
    6 我们始终尊重原创作者的版权,所有文章在发布时,均尽可能注明出处与作者。
    7 站长邮箱:laylwenl@gmail.com
    评论(没有评论)