共计 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 写法
关于 reactive
和 ref
的使用,我目前的用法是复杂数据类型我更愿意使用 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
封装组合式 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 的两种写法