Lzh on GitHub

组件之间通信

Reka UI 组件库中各个组件之间的通信机制。

Reka UI 组件主要通过以下几种方式进行通信:

Context Provider/Inject 模式

组件使用 Vue 的 provide/inject 模式在组件树中共享状态和方法。 CalendarRoot.vue:13-45 每个根组件都会创建一个 context 来提供共享的状态和回调函数给子组件使用。 SelectRoot.vue:59-60

type CalendarRootContext = {
  locale: Ref<string>
  modelValue: Ref<DateValue | DateValue[] | undefined>
  placeholder: Ref<DateValue>
  pagedNavigation: Ref<boolean>
  preventDeselect: Ref<boolean>
  grid: Ref< Grid<DateValue>[]>
  weekDays: Ref<string[]>
  weekStartsOn: Ref<0 | 1 | 2 | 3 | 4 | 5 | 6>
  weekdayFormat: Ref<WeekDayFormat>
  fixedWeeks: Ref<boolean>
  multiple: Ref<boolean>
  numberOfMonths: Ref<number>
  disabled: Ref<boolean>
  readonly: Ref<boolean>
  initialFocus: Ref<boolean>
  onDateChange: (date: DateValue) => void
  onPlaceholderChange: (date: DateValue) => void
  fullCalendarLabel: Ref<string>
  parentElement: Ref<HTMLElement | undefined>
  headingValue: Ref<string>
  isInvalid: Ref<boolean>
  isDateDisabled: Matcher
  isDateSelected: Matcher
  isDateUnavailable?: Matcher
  isOutsideVisibleView: (date: DateValue) => boolean
  prevPage: (prevPageFunc?: (date: DateValue) => DateValue) => void
  nextPage: (nextPageFunc?: (date: DateValue) => DateValue) => void
  isNextButtonDisabled: (nextPageFunc?: (date: DateValue) => DateValue) => boolean
  isPrevButtonDisabled: (prevPageFunc?: (date: DateValue) => DateValue) => boolean
  formatter: Formatter
  dir: Ref<Direction>
}

export const [injectSelectRootContext, provideSelectRootContext]
    = createContext<SelectRootContext<AcceptableValue>>('SelectRoot')

事件发射机制

组件通过 Vue 的 emit 系统向父组件发送事件通知状态变化。 RangeCalendarRoot.vue:107-114 例如,当日期选择发生变化时,会发射 update:modelValue 事件。

export type RangeCalendarRootEmits = {
  /** Event handler called whenever the model value changes */
  'update:modelValue': [date: DateRange]
  /** Event handler called whenever the placeholder value changes */
  'update:placeholder': [date: DateValue]
  /** Event handler called whenever the start value changes */
  'update:startValue': [date: DateValue | undefined]
}

回调函数传递

context 中定义的回调函数允许子组件直接调用父组件的方法来更新状态。 CalendarRoot.vue:29-30 比如 onDateChangeonPlaceholderChange 函数。

type CalendarRootContext = {
  locale: Ref<string>
  modelValue: Ref<DateValue | DateValue[] | undefined>
  placeholder: Ref<DateValue>
  pagedNavigation: Ref<boolean>
  preventDeselect: Ref<boolean>
  grid: Ref< Grid<DateValue>[]>
  weekDays: Ref<string[]>
  weekStartsOn: Ref<0 | 1 | 2 | 3 | 4 | 5 | 6>
  weekdayFormat: Ref<WeekDayFormat>
  fixedWeeks: Ref<boolean>
  multiple: Ref<boolean>
  numberOfMonths: Ref<number>
  disabled: Ref<boolean>
  readonly: Ref<boolean>
  initialFocus: Ref<boolean>
  onDateChange: (date: DateValue) => void // 回调函数
  onPlaceholderChange: (date: DateValue) => void // 回调函数
  fullCalendarLabel: Ref<string>
  parentElement: Ref<HTMLElement | undefined>
  headingValue: Ref<string>
  isInvalid: Ref<boolean>
  isDateDisabled: Matcher
  isDateSelected: Matcher
  isDateUnavailable?: Matcher
  isOutsideVisibleView: (date: DateValue) => boolean
  prevPage: (prevPageFunc?: (date: DateValue) => DateValue) => void
  nextPage: (nextPageFunc?: (date: DateValue) => DateValue) => void
  isNextButtonDisabled: (nextPageFunc?: (date: DateValue) => DateValue) => boolean
  isPrevButtonDisabled: (prevPageFunc?: (date: DateValue) => DateValue) => boolean
  formatter: Formatter
  dir: Ref<Direction>
}

响应式状态共享

通过 Vue 的响应式系统,组件间共享 ref 对象来实现状态同步。 CalendarRoot.vue:15-16 当一个组件修改共享状态时,所有依赖该状态的组件会自动更新。

type CalendarRootContext = {
  locale: Ref<string>
  modelValue: Ref<DateValue | DateValue[] | undefined> // 响应式状态
  placeholder: Ref<DateValue>
  pagedNavigation: Ref<boolean>
  preventDeselect: Ref<boolean>
  grid: Ref< Grid<DateValue>[]>
  weekDays: Ref<string[]>
  weekStartsOn: Ref<0 | 1 | 2 | 3 | 4 | 5 | 6>
  weekdayFormat: Ref<WeekDayFormat>
  fixedWeeks: Ref<boolean>
  multiple: Ref<boolean>
  numberOfMonths: Ref<number>
  disabled: Ref<boolean>
  readonly: Ref<boolean>
  initialFocus: Ref<boolean>
  onDateChange: (date: DateValue) => void
  onPlaceholderChange: (date: DateValue) => void
  fullCalendarLabel: Ref<string>
  parentElement: Ref<HTMLElement | undefined>
  headingValue: Ref<string>
  isInvalid: Ref<boolean>
  isDateDisabled: Matcher
  isDateSelected: Matcher
  isDateUnavailable?: Matcher
  isOutsideVisibleView: (date: DateValue) => boolean
  prevPage: (prevPageFunc?: (date: DateValue) => DateValue) => void
  nextPage: (nextPageFunc?: (date: DateValue) => DateValue) => void
  isNextButtonDisabled: (nextPageFunc?: (date: DateValue) => DateValue) => boolean
  isPrevButtonDisabled: (prevPageFunc?: (date: DateValue) => DateValue) => boolean
  formatter: Formatter
  dir: Ref<Direction>
}

集合管理

某些组件使用集合模式来管理子项目的注册和通信。 ComboboxGroup.vue:32-38 子组件在挂载时注册到父组件的集合中,卸载时移除。

onMounted(() => {
  if (!rootContext.allGroups.value.has(id))
    rootContext.allGroups.value.set(id, new Set())
})
onUnmounted(() => {
  rootContext.allGroups.value.delete(id)
})