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

В недавно опубликованном опросе State of JS за 2022 год некоторые участники сообщества Vue снова обсуждают преимущества API-интерфейса опций и композиции, а также то, сдерживает ли переход Vue. Я начал размышлять о том, какой аспект API-интерфейса композиции Vue 3 позволил мне так легко работать продуктивно с минимальными усилиями.

Я думаю, что моя собственная производительность во Vue может быть разбита на простую модель «3x3», которая позволяет легко понять и быть продуктивным при создании приложений без необходимости думать о сложности «высшего порядка», которая может создать сопротивление.

Чтобы лучше понять, что я имею в виду, и Надя Макаревич, и Эми Бланкеншип написали замечательные статьи, в которых описываются некоторые из основных дорожных опасностей, на которые следует обратить внимание при работе с React. я сталкивался лично, а также на профессиональных проектах.







Эти типы опасностей просто не существуют в Vue, потому что рендеринг является оптимальным. В Vue почти никогда не приходится думать о побочных эффектах, чрезмерном рендеринге или оптимизации, требующей useMemo и useCallback, потому что Арек Наво резюмирует в своем превосходном и кратком описании, фундаментальное различие между Vue и React заключается в том, что у них перевернуты модели того, что (повторно) визуализируется.

В этой статье я хочу обрисовать простую модель 3x3 для понимания Vue.js, чтобы помочь вам строить лучше, быстрее и с меньшими усилиями. Если вы хотите увидеть пример кода, ознакомьтесь с этим репозиторием:



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

Модель 3x3

Тема Vue 3 всегда вызывает дискуссии об использовании reactive против ref против $ref и опций против API композиции. Существуют сложные возможности фреймворка, которые поддерживают широкий спектр вариантов использования и сборок.

Это не статья о сложности (мы не можем все быть такими блестящими и общительными, как Энтони Фу); то, что нам нужно, — это простая, легкая в понимании ментальная модель для разработчика с ворчливыми мозгами, которая позволит нам создавать 90 % приложений быстро, стабильно и с минимальными затратами. суетиться.

Моя собственная внутренняя ментальная модель Vue разбита на три слоя по три основных понятия в каждом или «3x3»:

  1. Строительные блоки высокого уровня: приложение, состояние и маршрутизация.
  2. Строительные блоки на уровне компонентов: шаблон, сценарий и CSS.
  3. Стандартные блоки реактивности уровня скрипта: ref, watch и computed.

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

1) Строительные блоки высокого уровня

Этот первый уровень описывает, как логика организована на уровне приложения: приложение, состояние и маршрутизация.

1.1) Приложение

Начнем с создания приложения:

cd code
mkdir vue-3x3
yarn create vite . --template vue-ts
yarn

Это создает три интересующих файла (совпадение??).

Вот наш файл src/index.html:

Что загружает src/main.ts:

Что монтирует src/App.vue к div в index.html:

Теперь мы можем просмотреть это приложение в браузере, запустив:

yarn dev

1.2) Государство

На данном этапе нет необходимости иметь глобальное состояние, но помимо прототипа вы обнаружите, что наличие глобального состояния почти всегда необходимо для облегчения боли от сверления пропсов (хотя В Vue также есть парадигма предоставления-внедрения тоже). В React есть несколько моделей и фреймворков, из которых можно выбирать при работе с состоянием. Некоторые из этих фреймворков также можно использовать во Vue, например nanostores или Valtio от Дайши Като. Но рекомендуемая государственная библиотека Vue — это превосходная Pinia.

Чтобы добавить Pinia в наше приложение, выполните следующее:

yarn add pinia

И обновляем наше приложение:

Pinia предоставляет общее состояние, используя концепцию «хранилищ», которых вы можете создавать столько, сколько хотите, и перекомпоновывать их по мере необходимости для вашего приложения.

Мы создадим простую модель и сохраним ее в src/stores/appStore.ts:

1.3) Маршрутизация

Маршрутизация — это просто сопоставление URL-адресов с представлениями и компонентами в нашем приложении. Рекомендуемый маршрутизатор Vue — Vue Router.

yarn add vue-router

Создадим три компонента (совпадение?!?!): src/pages/Login.vue, src/pages/Index.vue и src/components/ChecklistDialog.vue (как и пустой App.vue) и подключим наш роутинг:

Для такого простого приложения мы можем подключить наш роутер прямо в main.ts, но давайте создадим отдельный файл в src/router/index.ts:

И обновите наш src/main.ts, чтобы загрузить наш роутер:

Если мы запустим наше приложение сейчас:

yarn dev

Мы получаем наш маршрут входа в URL.

Это основа приложения SPA Vue, сведенная к основам.

2) Строительные блоки уровня компонентов

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

2.1) Шаблон

Хотя можно использовать JSX/TSX с Vue, я предпочитаю практику однофайлового компонента (SFC), когда шаблон отделяется от логики.

Давайте добавим ввод для имени пользователя и кнопку на нашу страницу входа:

И вот что мы получаем:

2.2) Сценарий

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

Нам понадобится следующее (опять же, в 3-х частях!):

  1. Привязать ввод к свойству
  2. Привязать состояние кнопки к значению свойства
  3. Функция для перехода на страницу индекса

Вот приложение:

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

Это очень фундаментальное различие между React и Vue. Вот то же приложение в React (без маршрутизации и глобального состояния):

Давайте посмотрим, что происходит, когда мы вводим имя пользователя в версии React:

Система реактивности React фактически концептуально «перевернута» из Vue. Причина, по которой disabled работает в строке 19, заключается в том, что перерисовывается весь компонент(как указывает Эми в комментариях, даже checkUsername функция перераспределяется) со значением username теперь «гидратируется» из useState() каждый раз, когда оно вызывается при перерисовке. В этом тривиальном случае это не имеет большого значения; вы никогда не заметите разницы в производительности или странных побочных эффектов, вызванных ошибочным вызовом функции. Но по мере усложнения любого приложения крайне важно понимать и управлять этим жизненным циклом; чем больше приложение и команда, тем сложнее сделать это хорошо.

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

2.3) ССЦ

В Vue вы можете включить CSS непосредственно в файл .vue SFC, и он может быть либо scoped для компонента, либо глобальным.

Давайте стилизуем наш ввод:

Две вещи, которые следует отметить:

  1. Атрибут scoped означает, что область действия CSS ограничена только этим компонентом (важно: это также означает вниз по дереву; дочерние компоненты не получают область действия CSS!).
  2. Видите, как мы можем использовать значения из нашего скрипта в нашем CSS? Если мы хотим сделать это реактивным, все, что нам нужно сделать, это вместо этого объявить const spacing = ref('8px')!

Результат:

3) Строительные блоки уровня сценария

Хотя глубина системы реактивности Vue может быть запутанной, в целом вам нужно использовать только три примитива:

  1. ref — мы уже видели это в действии. Вот как мы объявляем свойство реактивным.
  2. computed — так мы создаем реактивную проекцию (подумайте о том, как работает Array.map или оператор SQL SELECT для проецирования новой формы).
  3. watch — так мы можем следить за изменением состояния и выполнять дополнительную логику.

Есть также reactive и (теперь отброшенное) преобразование $ref, но использование только ref никогда не было для меня проблемой, и я считаю, что проще даже не беспокоиться о reactive против ref против $ref. Просто используйте ref. (Turas.app полностью построен только из этих трех реактивных примитивов).

В мире React прямое изменение свойств объектов в состоянии не одобряется. Рассмотрим Redux, который работает с неизменяемым состоянием, которое требует от вас отправки следующего снимка состояния. Таким образом, использование распространения и деструктуризации состояния является обычным явлением. Но с Vue это, как правило, создаст немного больше работы, поскольку деструктурирование реактивного объекта требует toRefs, чтобы снова сделать свойства реактивными. Лично мне легче отслеживать, где свойства находятся «в домашнем» или «корневом» состоянии, не деструктурируя состояние; Короче говоря: упростите свою ментальную модель и сосредоточьтесь только на трех примитивах.

Система реактивности Solid.js на самом деле очень тесно связана с этими тремя примитивами. createSignal = ref, createMemo = computed и createEffect = watch. Для меня это свидетельство того, насколько фундаментальными являются эти 3 примитива для любой действительно реактивной среды представления.

Чтобы продемонстрировать эти принципы, а также состояние, мы немного расширим наше приложение и заполним Index.vue, чтобы показать наш контрольный список, и ChecklistDialog.vue, где мы можем создать/отредактировать один контрольный список.

3.1 Реактивность через ref()

Мы уже видели это в действии. ref эквивалентен паре [state, setState] в React. Но обратите внимание, что когда мы читаем или записываем значение, нам нужно использовать .value:

В шаблоне для нас уже развернуто значение:

3.2) Реактивность через вычисление()

Проще всего думать о computed как о реактивной проекции нашего состояния во многом так же, как SELECT является проекцией таблицы или Array.map позволяет нам проецировать форму массива объекты одной формы в массив других форм.

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

Вот состояние нашего приложения:

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

И вот результат:

Но computed не ограничивается простыми примитивами; что, если мы хотим отобразить список как отсортированный и отфильтрованный список? computed снова!

Наша проекция списка computed объединяет операции filter и sort, которые используют значение hidePrivate, привязанное к нашему новому флажку (строка 12).

Вот как он себя ведет:

Мы даже можем реорганизовать это и вместо этого переместить вычисляемое состояние в наше хранилище!

Что продуктивно в computed Vue, так это то, что он автоматически отслеживает зависимости для нас, и нет необходимости явно определять зависимости.

3.3) Реактивность через watch()

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

Давайте заполним наш компонент ChecklistDialog.vue:

Мы следим за состоянием app.selectedChecklist, и когда оно окажется правдой, мы настроим открытие этого диалога через свойство visible.

А теперь добавьте его в наше приложение:

Вот наше простое приложение с контрольным списком:

Строго говоря, нам не нужно было использовать watch в этом примере, потому что мы можем просто создать visible как computed, но watch позволяет нам выполнять другой код при изменении значения. Вместо этого мы могли бы также передать выбранное значение как prop, но это создает зависимость между компонентами, что может затруднить рефакторинг нашего дерева компонентов. С помощью этого паттерна мы можем переместить диалоговое окно в любое место нашего дерева (например, прямо в App.vue в корень).

Я надеюсь, что эта простая ментальная модель «3x3» поможет вам лучше понять, почему Vue — это такой продуктивный интерфейсный фреймворк для работы и почему я предпочитаю его, когда у меня есть такая роскошь. Эта простая модель снижает большую часть сложности, которая может понадобиться командам только в крайних случаях.

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

Если вам понравилась эта статья, подпишитесь на нее, ознакомьтесь с другими моими размышлениями о React, Vue и разработке приложений. Вы также можете найти меня в Twitter @chrlschnи LinkedIn.