Кодирование лучших составных частей: гибкие аргументы (2/5)

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

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

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

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

Вот некоторые передовые методы компоновки Vue, которые мы рассмотрим в этой статье:

  1. Как использовать параметр объекта параметров, чтобы сделать ваши компонуемые объекты более настраиваемыми
  2. Использование ref и unref, чтобы сделать наши аргументы более гибкими 👈 мы здесь
  3. Простой способ сделать возвращаемые значения более полезными
  4. Почему, начиная с интерфейса, ваши компонуемые компоненты становятся более надежными
  5. Как использовать асинхронный код без ожидания — сделать ваш код более понятным

Но сначала давайте убедимся, что мы все понимаем, что такое компонуемые.

Если вы уже прочитали предыдущую статью, вы можете перейти к следующему разделу.

Что такое компонуемый?

Согласно документации Vue, компонуемый — это функция, которая использует Vue Composition API для инкапсуляции и повторного использования логики с отслеживанием состояния.

Это означает, что любой код, использующий реактивность, можно превратить в компонуемый.

Вот простой пример компонуемого useMouse из документации Vue.js:

import { ref, onMounted, onUnmounted } from 'vue'
export function useMouse() {
  const x = ref(0)
  const y = ref(0)
  function update(event) {
    x.value = event.pageX
    y.value = event.pageY
  }
  onMounted(() => window.addEventListener('mousemove', update))
  onUnmounted(() => window.removeEventListener('mousemove', update))
  return { x, y }
}

Мы определяем наше состояние как refs, а затем обновляем это состояние при каждом движении мыши. Возвращая ссылки x и y, мы можем использовать их внутри любого компонента (или даже другого компонуемого).

Вот как мы будем использовать этот компонуемый внутри компонента:

<template>
  X: {{ x }} Y: {{ y }}
</template>
<script setup>
  import { useMouse } from './useMouse';
  const { x, y } = useMouse();
</script>

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

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

Используйте ref и unref для более гибких параметров.

Почти все компонуемые требуют в качестве входных данных какой-либо аргумент. Часто это реактивный ref. Это также может быть примитивный тип Javascript, такой как строка, число или объект. Но мы хотим, чтобы наши компонуемые были еще более гибкими и пригодными для повторного использования, верно?

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

// Works if we give it a ref we already have
const countRef = ref(2);
useCount(countRef);
// Also works if we give it just a number
const countRef = useCount(2);

Компонуемый элемент useTitle, который мы видели в предыдущей статье, также применяет этот шаблон.

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

const title = ref('This is the title');
useTitle(title);
title.value = 'New title please';

Если вы передадите только строку, она создаст для вас новый ref, а затем продолжит связывать его с заголовком документа:

const title = useTitle('This is the title');
title.value = 'New title please';

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

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

Реализация гибких аргументов в составном

Чтобы заставить шаблон гибких аргументов работать, нам нужно использовать либо функцию ref, либо функцию unref для полученного аргумента:

// When we need to use a ref in the composable
export default useMyComposable(input) {
  const ref = ref(input);
}
// When we need to use a raw value in the composable
export default useMyComposable(input) {
  const rawValue = unref(input);
}

Функция ref создаст для нас новый ref. Но если мы передаем ему ref, он просто возвращает нам это ref:

// Create a new ref
const myRef = ref(0);
// Get the same ref back
assert(myRef === ref(myRef));

Функция unref работает так же, но вместо этого она либо разворачивает ref, либо возвращает нам наше примитивное значение:

// Unwrap to get the inner value
const value = unref(myRef);
// Returns the same primitive value
assert(value === unref(value));

Давайте посмотрим, как некоторые компонуемые элементы из VueUse реализуют этот шаблон. VueUse — это коллекция компоновок с открытым исходным кодом для Vue 3, и она очень хорошо написана. Это отличный ресурс, чтобы научиться писать великолепные составные части!

useTitle

Мы вернемся к составному элементу useTitle, так как мы уже знакомы с ним.

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

// Pass in a string
const titleRef = useTitle('Initial title');
// Pass in a ref of a string
const titleRef = ref('Initial title');
useTitle(titleRef);

В исходном коде вы можете видеть, что сразу после того, как мы деструктурировали наш объект параметров, мы создали title ref. Здесь мы используем функцию ref, которая позволяет нам использовать либо ref, либо строку для создания ссылки title:

// ...
const title = ref(newTitle ?? document?.title ?? null)
// ...

Синтаксис ?? — это нулевой оператор объединения — причудливо звучащее название для если значение слева нулевое или неопределенное, используйте значение справа. Итак, эта строка сначала пытается использовать newTitle, но если это не определено, будет использоваться document.title, а если это не определено, она сдастся и будет использовать null.

Кое-что интересное для вас, знатоков TypeScript:

Используемая здесь переменная newTitle имеет тип MaybeRef<string>. Вот что тип определяется как:

type MaybeRef<T> = T | Ref<T>

Это определение типа означает, что тип MaybeRef<string> может быть либо string, либо Ref<string>, который является ссылкой со строковым значением внутри.

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

useCssVar

Компонуемый useCssVar позволяет нам получить значение переменной CSS и использовать его в нашем приложении:

const backgroundColor = useCssVar('--background-color');

Однако, в отличие от useTitle, здесь нам нужно строковое значение, чтобы мы могли найти переменную CSS в DOM. Используя функцию unref, этот составной объект может обрабатывать как ссылки, так и передаваемые строки:

// Using a string
const backgroundColor = useCssVar('--background-color');
// Using a ref
const cssVarRef = ref('--background-color');
const backgroundColor = useCssVar(cssVarRef);

Глядя на исходный код, мы видим, что для этого используется функция unref. На самом деле, он использует вспомогательную функцию под названием unrefElement, чтобы убедиться, что мы получаем элемент DOM, а не просто экземпляр Vue.

Большинство составных объектов в VueUse реализуют этот шаблон, если вы хотите изучить его подробнее. Так что выбирайте тот, который выглядит интереснее, и погрузитесь в код!

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

Мы только что потратили некоторое время на изучение второго шаблона в серии, где мы можем использовать аргументы более гибко, разумно используя ref и unref в наших составных элементах. Компонуемый по-прежнему будет работать независимо от того, есть ли у вас ref или просто необработанное значение Javascript. Он адаптируется к тому, как вы его используете!

Мы также рассмотрели, как библиотека VueUse реализует этот шаблон в компонуемых объектах useTitle и useCssVar. Компонуемый useTitle использует функцию ref, а useCssVar использует функцию unref, чтобы мы могли видеть оба варианта в действии.

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

// Returns a single value
const isDark = useDark();
// Returns an object of values
const {
  counter,
  pause,
  resume,
} = useInterval(1000, { controls: true });

Этот шаблон может значительно упростить использование составного объекта, особенно если большую часть времени вам нужно только одно значение.

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