Расширение компонентов Vue.js SFC - PullRequest
1 голос
/ 11 июля 2019

Я не нашел хорошего ресурса по расширению компонентов Vue.js.В каждом проекте, над которым я работал, независимо от используемой библиотеки компонентов пользовательского интерфейса, есть базовые компоненты приложения, которые расширяют компоненты библиотеки пользовательского интерфейса для обеспечения соблюдения стандартов и стандартов компании / приложения.

Я пытаюсь расширитьVue-Multiselect: https://vue -multiselect.js.org / , который имеет около 30 реквизитов и 12 слотов.Компонент, который я расширяю, не имеет значения - я упоминаю об этом только потому, что в идеале я не хочу повторять 30 пропов и 12 слотов в моей реализации.

Я просто хочу внести два изменения вПоведение компонента:

Сделать disabled более умным для поддержки

Компонент Vue-Multiselect имеет стандартную disabled поддержку, которая работает как положено:

<Multiselect :disabled="isDisabled" ...>

В нашем приложении у нас есть глобальное состояние в Vuex, которое определяет, доступно ли приложение только для чтения.Чего я хочу избежать, так это требовать, чтобы разработчики передавали это состояние в каждое поле формы:

<Multiselect :disabled="readOnly || isDisabled" ...>
<OtherComponent :disabled="readOnly || someOtherCondition" ...>
...

Таким образом, пользователь моего базового компонента должен заботиться только о своем локальном состоянии пользовательского интерфейса, которое влияет на отключенное состояние:

<BaseCombo :disabled="!emailValid" ...>

Это будет обрабатывать 90% полей форм, которые заблокированы, когда приложение доступно только для чтения, и я могу использовать дополнительную опору для случаев, когда мы хотим игнорировать глобальный статус только для чтения..

<BaseCombo :disabled="!emailValid" :ignoreReadOnly="true" ...>

Укажите значения по умолчанию

Во-вторых, я просто хочу переопределить некоторые значения проп по умолчанию.Этот пост посвящен вопросу предоставления значений по умолчанию:

https://stackoverflow.com/a/52592047/695318

И это прекрасно работало, пока я не попытался изменить поведение отключенного пропеллера, о котором я упоминал ранее.


Моя попытка решить эту проблему состояла в том, чтобы либо обернуть, либо расширить компонент.Я действительно хотел бы избежать повторного выделения всех реквизитов, если это возможно.

<template>
  <Multiselect
    :disabled="myCustomDisabled"
    :value="value"
    @input="$emit('input', $event)"
    :options="options"
    :label="label"
    :track-by="trackBy"
    :placeholder="placeholder"
    ... repeat for all 30 options

<script>
import Multiselect from 'vue-multiselect'

export default {
  name: "BaseCombo",
  extends: Multiselect, // extend or simply wrap?
  computed: {
    myCustomDisabled() {
      this.props.disabled || ... use disabled from Vuex state
    }
  },
  props: {
    disabled: Boolean,
    placeholder: {
      type: String,
      default: 'My Default Value',
    },
  ... repeat for all props

Проблема, с которой я столкнулся, заключается в том, что я не знаю, как обращаться с слотами .Пользователь этого BaseCombo должен все еще иметь возможность использовать все 12 слотов в компоненте VueMultiselect.

Есть ли лучшее решение для расширения компонентов?

Ответы [ 3 ]

1 голос
/ 12 июля 2019

Состав компонентов более гибкий, но для тех, кто хочет использовать более простую опцию extends (см. Расширение компонентов VueJS ), это пример.

Примечания

  • все свойства VueMultiselect передаются по умолчанию, но мы переопределяем disabled
  • опустить шаблон в расширении для наследования шаблона VueMultiselect
  • передать таблицу стилей VueMultiselect как scoped (не наследуется по умолчанию)
  • слот просто работает
  • слушатели просто работают
<script>
import VueMultiselect from 'vue-multiselect'

export default {
  name: 'MultiselectExtended',
  extends: VueMultiselect,
  props: {
    disabled: {
      type: Boolean,
      default: function() {
        return this.$store.getters.isReadOnly
      }
    },
  },
}
</script>

<style src="vue-multiselect/dist/vue-multiselect.min.css" scoped></style>
0 голосов
/ 11 июля 2019

Вот полный пример, основанный на ответе Sumurai8 и комментариях motia.

<template>
  <Multiselect v-bind="childProps" v-on="$listeners">
    <slot v-for="(_, name) in $slots" :name="name" :slot="name" />
    <template v-for="(_, name) in $scopedSlots" :slot="name" slot-scope="slotData">
      <slot :name="name" v-bind="slotData" />
    </template>
  </Multiselect>
</template>

<script>
  import Multiselect from 'vue-multiselect'

  export default {
    name: "BaseCombo",
    props: {
      placeholder: {
        type: String,
        default: 'This is my default',
      },
      disabled: {
        type: Boolean,
        default: false,
      },
    },
    components: {
      Multiselect,
    },
    computed: {
      childProps() {
        return { ...this.$props, ...this.$attrs, disabled: this.isDisabled };
      },
      appReadOnly() {
        return this.$store.state.appReadOnly;
      },
      isDisabled() {
        return this.disabled || this.appReadOnly;
      }
    },
  }
</script>
0 голосов
/ 11 июля 2019

Вы можете использовать this.$props для доступа к реквизитам, определенным в атрибуте реквизитов. Точно так же вы можете получить доступ к атрибутам (вещи, которые вы не определили как реквизиты) с помощью this.$attrs. Наконец, вы можете связать реквизит с v-bind="someVariable".

Если вы объедините это, вы можете сделать что-то вроде этого:

<!-- App.vue -->
<template>
  <component-a msg="Hello world" :fancy="{ test: 1 }" />
</template>
<!-- ComponentA.vue -->
<template>
  <component-b v-bind="$attrs" />
</template>

<script>
export default {
  name: 'componentA'
}
</script>
<!-- ComponentB.vue -->
<template>
  <div>
    {{ msg }}
    {{ fancy }}
  </div>
</template>

<script>
export default {
  props: {
    msg: String,
    fancy: Object
  },
  mounted () {
    console.log(this.$props);
  }
}
</script>

В этом примере компонент B будет компонентом, который вы пытаетесь расширить.

...