Кодирование лучших составных частей: начните с интерфейса (4/5)

В этой серии статей вы познакомитесь с несколькими передовыми методами написания составных объектов. Когда вы закончите, у вас будет четкое представление о создании твердых составных частей.

Автор: Майкл Тиссен

Компоновка, которую вы написали в прошлом месяце, возвращает объект, но сейчас вам действительно нужно, чтобы она вместо этого возвращала массив. Но уже поздно что-то менять. Это потребовало бы буквально дней рефакторинга, поскольку компонуемый код уже присутствует во всей кодовой базе. Если бы вы вначале потратили немного больше времени, чтобы действительно понять, как вы хотите использовать компонуемый объект, вы могли бы спасти себя от этого разочарования…

В этой статье мы узнаем о написании интерфейсов для наших составных объектов и о том, как этот процесс может помочь нам создавать составные объекты, которые будут служить нам в далеком будущем. Эта статья является четвертой в серии из пяти частей, посвященных лучшему кодированию компонуемых компонентов. Если вы пропустили первые три статьи, вам захочется начать сначала. В этой серии статей вы познакомитесь с несколькими передовыми методами написания составных объектов. После того, как вы закончите, вы четко поймете, как создавать твердые компонуемые объекты.

Теперь, без лишних слов, давайте перейдем к интерфейсу.

Сначала напишите интерфейс

Вместо того, чтобы начинать с реализации, вы должны начать с того, как вы хотите, чтобы составной объект использовался. Вот что я имею в виду, когда говорю «начните с интерфейса». Это API компонуемого. Это граница между компонуемой и другими частями вашего приложения.

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

Это важно, потому что через 6 месяцев изменить интерфейс намного сложнее, чем реализовать. Так что лучше потратить дополнительное время на этот шаг, чтобы сделать все правильно.

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

(Но, к сожалению, мы никогда не можем предсказать будущее 😢)

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

  1. Какие аргументы мы передаем в наш составной объект? Ссылка, необработанное значение или серия значений?
  2. Какие параметры должны быть включены в объект параметров?
  3. Какие значения возвращает наш компонуемый объект? Это просто одно значение или мы хотим использовать шаблон динамического возвращаемого значения?

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

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

Так почему бы нам не рассмотреть пример, чтобы увидеть этот процесс в действии?

Разбираемся с интерфейсом useMouse

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

Вопрос 1: Какие аргументы мы передаем в наш компонуемый объект?

Ответ на этот вопрос прост — составной объект useMouse не принимает никаких аргументов. Вместо этого мы хотим, чтобы координаты мыши возвращались к нам и обновлялись реактивно.

Вопрос 2: Какие параметры должны быть включены в объект параметров?

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

Когда дело доходит до экранных координат, возникает отличный вопрос: относительно чего? С помощью координат мыши мы можем получить их относительно:

  • Область просмотра браузера
  • Весь документ
  • Конкретный элемент

Итак, у нас должен быть второй вариант — target — который позволяет нам выбирать, на чем мы основываем наши координаты. Хорошим значением по умолчанию, вероятно, будет область просмотра браузера.

Вопрос 3: Какие значения возвращает компонуемый объект?

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

Может быть, мы начнем с захвата координат как объекта:

const { x, y } = useMouse();

По мере того, как мы продолжаем прототипировать, мы понимаем, что нам также нужно знать, щелкнули мышью или нет, поэтому мы добавим это в:

const { x, y, isClicked } = useMouse();

Это работает, но как-то коряво. Продолжая писать наше приложение, мы понимаем, что нам действительно нужно получить координаты мыши в отдельном объекте:

const { position, isClicked } = useMouse();

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

const {
  position,
  positionArr,
  isClicked,
} = useMouse();

На данный момент у нас есть довольно хорошее представление о том, как мы будем использовать компонуемый объект useMouse. Но, конечно, требования со временем меняются, так что это не высечено на камне. Но вы можете увидеть, насколько сильно изменился наш интерфейс, немного поработав над прототипом. Если бы мы просто согласились с нашим первым предположением, мы могли бы написать компонуемый код, который вскоре потребовал бы рефакторинга.

Подведение итогов

В этой статье мы узнали, что, критически подумав о том, как мы хотим использовать наш составной объект в начале, мы можем написать лучшую реализацию. А написав лучшую реализацию, мы сэкономим себе время и нервы в будущем. Нам не нужно будет столько рефакторить, потому что мы хорошо продумали, как мы хотим использовать компонуемый.

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

В следующей и последней статье этой серии будет рассказано, как мы можем упростить async код в наших компонуемых объектах, предотвратив кучу ошибок и запутанных ситуаций:

const count = ref(0);
// This async data fetch won't interfere with our reactivity
const { state } = useAsyncState(fetchData());
const doubleCount = computed(() => count * 2);

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

Первоначально опубликовано на https://www.vuemastery.com 16 мая 2022 г.