Интересные новинки Vue 3

от автора

Вместо предисловия

Vue используется во всех проектах FunCorp. Мы внимательно следим за развитием фреймворка, постоянно улучшаем процесс разработки и внедряем лучшие практики. И, конечно же, мы не могли пройти мимо и не перевести статью Филиппа Раковски, сооснователя VueStorefront, про новые фичи Vue 3, серьёзно влияющие на написание кода.

image
В прошлый раз мы рассматривали фичи, которые влияют на производительность Vue 3. Нам уже известно, что приложения, написанные на новой версии фреймворка, работают очень быстро, но производительность — не самое важное изменение. Для большинства разработчиков намного важнее то, как Vue 3 повлияет на стиль написания кода.

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

Давайте начнём с API, о котором многие из вас могли слышать.

Composition API

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

Сейчас мы создаём компоненты с синтаксисом, который называется Options API. Для того чтобы добавить логику, мы создаём свойства (опции) в объекте компонента, например data, methods, computed и т.д. Основным недостатком данного подхода является то, что это не JavaScript-код как таковой. Вам необходимо точно знать, какие опции доступны в шаблоне и каким будет поведение this. Компилятор Vue преобразует свойства в работающий JavaScript-код за вас. Из-за этой особенности мы не можем в полной мере пользоваться автодополнением или проверкой типов.

Composition API решает эту проблему и даёт возможность использовать механизмы, доступные через опции, с помощью обыкновенных JavaScript-функций.
Команда Vue описывает Composition API как «дополнительный, основанный на функциях API, позволяющий гибко использовать композицию в логике компонента». Код, написанный с помощью нового API, лучше читается, что делает его более лёгким для понимания.

Чтобы разобраться в том, как работает новый синтаксис, рассмотрим пример простого компонента.

<template>   <button @click="increment">     Count is: {{ count }}, double is {{ double }}, click to increment.   </button> </template>  <script> import { ref, computed, onMounted } from 'vue'  export default {   setup() {     const count = ref(0)     const double = computed(() => count.value * 2)      function increment() {       count.value++     }      onMounted(() => console.log('component mounted!'))      return {       count,       double,       increment     }   } } </script> 

Разобьём код на части и разберём, что же здесь происходит.

import { ref, computed, onMounted } from 'vue'

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

Возможно, у вас возникнет вопрос: что это за таинственный метод setup?

export default {   setup() {} }

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

К тому, что мы не вернём из setup, доступа в шаблоне не будет.

const count = ref(0)

Реактивное свойство count инициализируем с помощью функции ref. Она принимает примитив или объект и возвращает реактивную ссылку. Переданное значение будет сохранено в свойстве value созданной ссылки. Например, если мы хотим получить доступ к значению count, нам необходимо явно обратиться к count.value.

const double = computed(() => count.value * 2)   function increment() {   count.value++ } 

Так мы объявляем вычисляемое свойство double и функцию increment.

onMounted(() => console.log('component mounted!'))

C помощью хука onMounted мы выводим в консоль сообщение после монтирования компонента для демонстрации такой возможности.

return {   count,   double,   increment }

Чтобы свойства count и double и метод increment были доступны в шаблоне, возвращаем их из метода setup.

<template>   <button @click="increment">     Count is: {{ count }}, double is {{ double }}. Click to increment.   </button> </template>

И вуаля! У нас есть доступ к свойствам и методам из setup, точно так же, как если бы они были объявлены через старый Options API.

Это простой пример, подобное можно было бы легко написать и с помощью Options API.
Но преимущество нового Composition API не столько в возможности писать код в другом стиле, сколько в возможностях, открываемых для повторного использования логики.

Переиспользование кода с Composition API

Давайте подробнее рассмотрим преимущества нового Composition API, например, для переиспользования кода. Сейчас, если мы хотим использовать какой-то кусок кода в нескольких компонентах, у нас есть два варианта: миксины (mixins) и слоты с ограниченной областью видимости (scoped slots). Оба варианта имеют свои недостатки.

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

Для начала рассмотрим реализацию с использованием миксинов.

import CounterMixin from './mixins/counter'   export default {   mixins: [CounterMixin] }

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

Теперь рассмотрим слоты с ограниченной областью видимости.

<template>   <Counter v-slot="{ count, increment }">      {{ count }}     <button @click="increment">Increment</button>    </Counter>  </template>

При использовании слотов мы в точности знаем, к каким свойствам мы имеем доступ через директиву v-slot, что достаточно просто понять. Недостаток этого подхода в том, что мы можем получить доступ только к данным компонента Counter.

А теперь рассмотрим реализацию с использованием Composition API.

function useCounter() {   const count = ref(0)   function increment () { count.value++ }     return {     count,     incrememt   } }   export default {   setup () {     const { count, increment } = useCounter()     return {       count,       increment     }   } }

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

Лучше выглядит и использование сторонних библиотек. Например, если мы хотим использовать Vuex, то можем явно импортировать функцию useStore и не засорять прототип Vue свойством this.$store. Этот подход позволяет избавиться от дополнительных манипуляций в плагинах.

const { commit, dispatch } = useStore()

Если вы хотите узнать больше о Composition API и его применениях, я рекомендую прочитать документ, в котором команда Vue объясняет причины создания нового API и предлагает кейсы, в которых он пригодится. Также есть замечательный репозиторий с примерами использования Composition API от Thorsten Lünborg, одного из членов команды ядра Vue.

Изменения в конфигурировании и монтировании

В новом Vue есть другие важные изменения в том, как мы создаём и конфигурируем наше приложение. Давайте рассмотрим это на примере.

import Vue from 'vue' import App from './App.vue'   Vue.config.ignoredElements = [/^app-/] Vue.use(/* ... */) Vue.mixin(/* ... */) Vue.component(/* ... */) Vue.directive(/* ... */)   new Vue({   render: h => h(App) }).$mount('#app')

Сейчас мы используем глобальный объект Vue для конфигурирования и создания новых инстансов Vue. Любое изменение, сделанное нами в объекте Vue, будет затрагивать конечные инстансы и компоненты.

Рассмотрим, как это будет работает во Vue 3.

import { createApp } from 'vue' import App from './App.vue'   const app = createApp(App)   app.config.ignoredElements = [/^app-/] app.use(/* ... */) app.mixin(/* ... */) app.component(/* ... */) app.directive(/* ... */)   app.mount('#app')

Как вы уже заметили, конфигурация относится к конкретному инстансу Vue, созданному с помощью createApp.

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

Эти изменения обсуждаются в RFC, и возможно, в будущем реализация будет другой.

Фрагменты

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

<template>   <div>Hello</div>   <div>World</div> </template>

Причиной является то, что инстанс Vue, скрывающийся за каждым компонентом, может быть прикреплён только к одному элементу DOM. Сейчас существует способ создать компонент с несколькими корневыми элементами: для этого необходимо написать компонент в функциональном стиле, которому не нужен собственный инстанс Vue.

Оказывается, такая же проблема существует и в React-сообществе, решена она была с помощью виртуального элемента Fragment.

Выглядит это так:

class Columns extends React.Component {   render() {     return (       <React.Fragment>         <td>Hello</td>         <td>World</td>       </React.Fragment>     );   } }

Несмотря на то, что Fragment выглядит как обычный DOM-элемент, он виртуальный и не будет создаваться в DOM-дереве. С этим подходом мы можем использовать функциональность одного корневого элемента без создания лишнего элемента в DOM.

Сейчас вы можете использовать фрагменты и во Vue 2, но с помощью библиотеки vue-fragments, а во Vue 3 они будут работать из коробки!

Suspense

Еще одна отличная идея из экосистемы React, которая будет реализована во Vue 3, — это Suspense.

Suspense приостанавливает рендеринг компонента и отображает заглушку до выполнения определённых условий. На конференции Vue London Эван Ю вскользь затронул Suspense и показал API, который мы можем ожидать в будущем. Suspense-компонент будет иметь 2 слота: для контента и для заглушки.

<Suspense>   <template >     <Suspended-component />   </template>   <template #fallback>     Loading...   </template> </Suspense>

Заглушка будет отображаться до тех пор, пока компонент <Suspended-component/> не будет готов. Компонент Suspense также может ожидать загрузку асинхронного компонента или выполнения каких-то асинхронных действий в setup-функции.

Несколько v-models

v-model — это директива, с помощью которой можно использовать двусторонний биндинг. Мы можем передать реактивное свойство и изменить его внутри компонента.

Нам она хорошо известна по работе с элементами форм.

<input v-bind="property />

Но знали ли вы, что v-model можно использовать с любым компонентом? Под капотом v-model является лишь пробросом параметра value и прослушиванием события input.

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

<input    v-bind:value="property"   v-on:input="property = $event.target.value" />

Можно даже изменить названия свойства и события по умолчанию с помощью опции model:

model: {   prop: 'checked',   event: 'change' }

Как видно, директива v-model может быть очень полезным «синтаксическим сахаром», если мы хотим использовать двусторонний биндинг в наших компонентах. К сожалению, на компонент может быть лишь одна v-model.

К счастью, во Vue 3 эта проблема будет решена. Мы сможем передать имя в v-model и использовать столько v-model, сколько необходимо.

Пример использования:

<InviteeForm   v-model:name="inviteeName"   v-model:email="inviteeEmail" />

Эти изменения обсуждаются в RFC, и возможно, в будущем реализация будет другой.

Portals

Порталы — это компоненты, созданные для рендера контента вне иерархии текущего компонента. Это тоже одна из возможностей, реализованных в React. В документации React порталы описываются следующим образом: «Порталы позволяют рендерить дочерние элементы в DOM-узел, который находится вне DOM-иерархии родительского компонента».

Порталы отлично подходят для реализации таких компонентов, как модальные окна, попапы и всех тех, которые необходимо отобразить поверх страницы.

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

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

Ниже представлен вариант реализации на библиотеке portal-vue, которая добавляет порталы во Vue 2.

<portal to="destination">   <p>This slot content will be rendered wherever thportal-target with name 'destination'     is  located.</p> </portal>   <portal-target name="destination">   <!--   This component can be located anywhere in your App.   The slot content of the above portal component wilbe rendered here.   --> </portal-target>

А во Vue 3 данная фича будет из коробки.

Новое API пользовательских директив

API пользовательских директив немного изменится во Vue 3, чтобы больше соответствовать жизненному циклу компонента. Создание директив станет более интуитивным, а значит, и более простым для понимания и изучения новичками.

Сейчас объявление пользовательской директивы выглядит так:

const MyDirective = {   bind(el, binding, vnode, prevVnode) {},   inserted() {},   update() {},   componentUpdated() {},   unbind() {} }

А во Vue 3 будет выглядеть так:

const MyDirective = {   beforeMount(el, binding, vnode, prevVnode) {},   mounted() {},   beforeUpdate() {},   updated() {},   beforeUnmount() {}, // new   unmounted() {} }

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

Этот API так же обсуждается и может измениться в будущем.

Резюме

Рядом со значительным нововведением — Composition API — мы можем найти несколько улучшений поменьше. Очевидно, что Vue движется в сторону улучшения опыта разработчика, к упрощению и интуитивизации API. Так же круто видеть, что команда Vue решила добавить в ядро фреймворка много идей, которые уже реализованы в сторонних библиотеках.

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


ссылка на оригинал статьи https://habr.com/ru/company/funcorp/blog/475968/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *