Задать вопрос
ReturnMeVoid
@ReturnMeVoid

Как правильно писать компоненты-обёртки во Vue 3?

Допустим у меня есть компонент из ui библиотеки - LibModal. Я хочу написать ему обёртку - MyModal.

Я определяю нужные в MyModal пропсы. Но мне нужно, чтобы MyModal принимал не только свои пропсы, но и пропсы LibModal (т.к. это его обёртка). И в таком случае у меня два варианта:

1) В defineProps компонента MyModal указать только новые (нужные обёртке) пропсы. В этом случае, все пропсы для LibModal (не указанные в MyModal, но принимаемые LibModal) можно будет передать в MyModal и они попадут в attrs (и присвоить их ). Этот вариант не подходит, так как не проверяет на типы передаваемые пропсы для LibModal, т.к. они не указаны в defineProps компонента MyModal. Также нет автокомплита из-за этого.

2) В defineProps компонента MyModal помимо его пропсов продублировать пропсы LibModal. В таком случае в пропсы для LibModal уже попадут не в attrs, а в основные пропсы. Но становится не понятно, как оптимально разделить общие пропсы на libModalProps и пропсы для MyModal (и сделать ).

Расскажите, как обычно вы пишете компоненты-обёртки над другими компонентами и как прокидываете их пропсы. Насколько я понял, первый вариант самый распространённый, но отсутствие типизации очень удручает в TS-проекте.

Спасибо за ответы!
  • Вопрос задан
  • 194 просмотра
Подписаться 1 Простой 1 комментарий
Пригласить эксперта
Ответы на вопрос 3
@null_object
Сначала надо чтобы компонент помимо своих типов наследовал типы дочернего компонента, тут понятно - либо extends, либо через пересечение типов или любой другой способ.
interface MyModalProps extends LibModalProps {
  isOpen: boolean;
}

Поскольку у родительского компонента будут и свои пропсы и дочернего, нужно их разделить и делегировать дочернему только его пропсы, сделать это можно разными способами, например в computed
const delegatedProps = computed(() => {
  // перечисляем пропсы родителя, остальные достаем через spread
  const { isOpen, ...libModalProps } = props;

  return libModalProps;
});

и передаем все пропсы в дочерний компонент
<LibModal v-bind="delegatedProps">
    ...
</LibModal>


Еще вариант использовать reactiveOmit

пример
пример с reactiveOmit из shadcn-ui
Ответ написан
Fragster
@Fragster
помогло? отметь решением!
Вот так работает (на примере quasar):

<template>
  <q-btn
    ref="btnRef"
    :text-color
    v-bind="parentProps"
    @click="onBtnClick"
  />
</template>


<script setup lang="ts">
import type {QBtnProps} from 'quasar'

type PropsType = QBtnProps & {
  textColor?: string,
}
const {textColor = 'black', ...parentProps} = defineProps<PropsType>()

const emit = defineEmits(['click'])
function onBtnClick() {
  console.log('Button clicked')
  emit('click')
}
</script>



либо с использованием https://github.com/vuejs/language-tools/blob/maste...
import {QBtn} from 'quasar'
import type { ComponentProps } from 'vue-component-type-helpers';

type PropsType = ComponentProps<typeof QBtn>  & {
  textColor?: string,
}
const {textColor = 'black', ...parentProps} = defineProps<PropsType>()

UPD: это не компилируется, хотя в VS code все подсказки работают (
Ответ написан
Я обычно пользуюсь первым вариантом
* главный плюс - внутри обертки я ничего не знаю и знать не хочу про те пропсы, которые я напрямую не использую
* отсутсвие автокомплита субъективно. Взять тот же квазар - даже если я вытащу и перечислю все три десятка пропсов, которыми обычно обладают их компоненты, я всё равно пойду на сайт с докой, что бы понять как называются нужные мне параметры и что туда передавать. А если дойду до такого, что запомню их - то и автокомплит мне уже будет не особо нужен
* ну и если оборачиваемый компонент обновит свои пропсы, мне придется править на одно место меньше - только там, где компонент используется, но не обертку

Если всё таки хочется заморачиваться со вторым вариантом
* есть варианты вытащить типы пропсов из компонента, что бы не объявлять их вручную ( InstanceType<typeof MyComponent>["$props"] вроде как должно сработать, но не проверял)
* деструктурировать объект props не нужно - в шаблоне все пропсы и так доступны
* чутка можно уменьшить количество символов за счет v-bind same-name shorthand:
<img :src :alt />
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы