Skip to content

Latest commit

 

History

History

how-to-build-vue-components-that-play-nice

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

Как писать Vue-компоненты, которые хорошо взаимодействуют

Перевод статьи Kevin Ball: How To Build Vue Components That Play Nice.

Очень мало людей, которые пишут Vue-компоненты, изначально собираются выложить их в открытый доступ. Большинство из нас начинают писать компоненты по принципу — у нас есть проблема, которую мы хотим решить, написав для этого компонент. Иногда мы хотим решить одну и ту же проблему в разных местах нашей кодовой базы, поэтому мы берём компонент и рефакторим его немного для того, чтобы сделать повторно используемым. Затем мы хотим использовать компонент в другом проекте и для этого переносим его в отдельный пакет. Тогда у нас возникает мысль: «А почему бы не поделиться этим компонентом с остальным миром?» и мы делаем наш компонент свободно доступным.

С одной стороны, это здорово, что означает большую и растущую доступность компонентов с открытым исходным кодом для всех, кто работает с Vue (поиск по «vue» на npmjs.com показывает более 13 000 пакетов).

С другой стороны, поскольку большинство из этих компонентов эволюционировали из конкретной ситуации, и не у всех из нас имеется опыт разработки компонентов для повторного использования в различных окружениях, многие из них не "хорошо взаимодействуют" (в оригинале "play nice" — прим. пер.) с экосистемой Vue.

Что значит "хорошо взаимодействует"? В общих чертах это означает, что он выглядит вполне естественным для разработчиков Vue, а также его легко расширить и интегрировать в любое приложение.

После изучения широкого набора различных компонентов с открытым исходным кодом, вот что я думаю о Vue-компонентах, которые хорошо взаимодействуют:

  1. Реализована совместимость с директивой v-model
  2. Компонент прозрачен для событий
  3. Есть возможность присваивать атрибуты необходимым элементам
  4. Придерживаются норм браузеров для навигации по клавиатуре
  5. Отдаётся предпочтение обработчикам событий, а не колбэкам
  6. Видимость стилей ограничена компонентом

Реализована совместимость с директивой v-model

Для компонентов, которые по сути являются полями формы — будь то автозаполнение полей для поиска, поля с календарём для ввода даты или что-то ещё, иначе говоря, реализуют дополнительную функциональность для одного поля, позволяющую пользователю указывать данные — одним из наиболее важных способов быть естественным для Vue является поддержка v-model.

Согласно руководству Vue по компонентам директива v-model на компонентах, в основном, работает через передачу свойства value с применением обработчика событий input.

Например, если бы мы реализовывали элемент ввода даты, который оборачивает поле ввода, то мы инициализировали бы его с использованием свойства value, а при выборе даты вызывали событие input, и в итоге код выглядел бы следующим образом:

import datepicker from 'my-magic-datepicker';

export default {
  props: ['value'],
  mounted() {
    datepicker(this.$el, {
      date: this.value,
      onDateSelected: (date) => {
        this.$emit('input', date);
      },
    });
  }
}

Компонент прозрачен для событий

Для реализации поддержки директивы v-model необходимо реализовать событие input. Но как насчёт других событий, таких как события нажатий с мыши и клавиатуры и т.д.? В то время как нативные события всплывают в HTML (DOM), обработчики событий во Vue по умолчанию не делают это.

Например, если я сделаю что-то подобное, это не сработает:

<my-textarea-wrapper @focus="showFocus">

До тех пор, пока мы не напишем код в компоненте-обёртке, который на самом деле генерирует событие focus, обработчик события showFocus никогда не будет вызван. Однако Vue предоставляет нам способ для доступа к применяемым компоненту обработчикам событий, поэтому мы можем присвоить в нужном месте объект $listeners.

<div class="my-textarea-wrapper">
  <textarea v-on="$listeners"></textarea>
</div>

Теперь события, которые происходят на текстовом многострочном поле, будут всплывать как обычно.

Есть возможность присваивать атрибуты необходимым элементам

Как насчёт таких атрибутов как rows для полей с текстовыми областями или title для добавления простой подсказки к любому атрибуту через соответствующий HTML-атрибут.

По умолчанию Vue принимает атрибуты, применяемые к атрибуту, и помещает их в корневой элемент этого компонента. Это не всегда то, что вам нужно. Однако, если мы снова посмотрим на обёртку над тегом textarea, то в этом случае имело смысл применять атрибуты к самому элементу, а не оборачивающему тегу div.

Чтобы сделать это, мы указываем компоненту не применять атрибуты по умолчанию, а вместо этого назначать их непосредственно, используя объект $attrs. В нашем JavaScript-коде:

export default {
  inheritAttrs: false,
}

А затем в нашем шаблоне:

<div class="my-textarea-wrapper">
  <textarea v-bind="$attrs"></textarea>
</div>

Придерживаются норм браузеров для навигации по клавиатуре

Доступность и навигация с помощью клавиатуры — одно из наиболее часто забываемых частей веб-разработки, и одновременно все они являются важными для правильно сделанного компонента, если вам нужен такой компонент, который хорошо взаимодействует в экосистеме.

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

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

Отдаётся предпочтение обработчикам событий, а не колбэкам

Когда дело до взаимодействия данными и пользовательскими действиями от вашего компонента к его родителям, то есть два распространённых варианта: колбэки во входных параметрах (они же свойства) и события. Поскольку пользовательские события не всплывают как нативные браузерные события, они функционально одинаковы, но для возможности повторного использования я почти всегда рекомендую использовать события вместо колбэков. Почему?

В одном выпуске подкаста Fullstack Radio Крис Фриц, один из разработчиков основной команды Vue, привёл следующие причины:

  1. Использование событий делают компонент более прозрачным в плане того, что должны о нём знать родительские компоненты. Он создаёт чёткое разделение между тем, что получено от родительских компонентов и тем, что отправлено им.
  2. Вы можете использовать выражения непосредственно в обработчиках событий, позволяя держать их очень компактными для обычных случаев.
  3. Это больше соответствует Vue — примеры и документация Vue обычно используют события для связи взаимодействия компонента с родительскими компонентами.

К счастью, если вы используете подход «колбэки в свойствах» (callbacks-in-props), довольно просто изменить компонент для генерации событий вместо этого. Компонент, использующий колбэки, может быть таким:

// файл my-custom-component.vue
export default {
  props: ['onActionHappened', ...]
  methods() {
    handleAction() {
      // остальной код...
      if (typeof this.onActionHappened === 'function') {
        this.onActionHappened(data);
      }
    }
  }
}

Далее при его использование может выглядит так:

<my-custom-component :onActionHappened="actionHandler" />

Переход на новый подход, основанный на событиях, будет выполнен следующим образом:

// файл my-custom-component.vue
export default {
  methods() {
    handleAction() {
      // остальной код...
      this.$emit('action-happened', data);
    }
  }
}

И использование компонента изменится на:

<my-custom-component @action-happened="actionHandler" />

Видимость стилей ограничена компонентом

Однофайловые компоненты позволяют встраивать стили непосредственно в компоненты, и особенно в сочетании с ограничением области видимости CSS-стилей через атрибут scoped даёт нам отличный способ использовать полностью упакованные, стилизованные компоненты таким образом, чтобы они не влияли на другие части приложения.

Благодаря такой возможности у вас может возникнуть соблазн поместить все стили в компонент и отправлять в открытый доступ такой полностью стилизованный компонент. Здесь следующая проблема: не все стили приложения одинаковые, и те стили, благодаря которым ваше приложение выглядит хорошо, в чьём-то другом приложении будут выглядеть совсем наоборот.

Для предотвращения этого я рекомендую сделать так, чтобы все структурные CSS-стили (цвета, рамки, тени и т.д.) были либо исключены из компонента, либо должны быть доступны для отключения в нём. Как вариант, рассмотрите возможность использования настраиваемого SCSS-файла (partial), который позволит пользователям вашего компонента стилизовать его так, как это будет необходимо.

Недостаток использования внешнего SCSS-файла заключается в том, что вашим пользователям потребуется подключать этот файл в свою систему сборки для компиляции стилей в CSS, иначе им будет показан компонент без стилей. Для получения лучшего из обоих миров, вы можете ограничить область действия стилей в компонента служебным классом, который будет передаваться через входной параметр (свойство компонента), для отключения стилей по умолчанию и удовлетворения потребностей тех пользователей, которые хотят иметь собственное оформление компонента. Если вы создаёте SCSS-стили в виде миксина, то вы могли бы использовать одинаковые SCSS-стили для пользователей, которым требуется большая гибкость для стилизации компонента.

<template>
  <div :class="isStyledClass">
    <!-- мой компонент -->
  </div>
</template>

Далее в вашем JavaScript-коде компонента:

export default {
  props: {
    disableStyles: {
      type: Boolean,
      default: false
    }
  },
  computed: {
    isStyledClass() {
      if (!this.disableStyles) {
        return 'is-styled';
      }
  },
}

Тогда вы сможете использовать так:

@import 'my-component-styles';

.is-styled {
  @include my-component-styles();
}

Данное решение позволит стилизовать компонент из коробки, но даёт возможность пользователям определять собственную стилизацию компонента с помощью установки свойства disableStyles в значение true без всякого переопределения стилей с более высокой специфичностью, либо использовать ваш миксин с их стилями, или вообще полностью с нуля стилизовать компонент.


Слушайте наш подкаст в iTunes и SoundCloud, читайте нас на Medium, контрибьютьте на GitHub, общайтесь в группе Telegram, следите в Twitter и канале Telegram, рекомендуйте в VK и Facebook.