Skip to content

Приложение: Миграция с Vue 2

Переход приложения Vue 2 на Vue 3 не так прост, как простая замена фреймворка. Хотя API Options остается полностью совместимым, и нет необходимости переходить на API Composition, есть и другие изменения, о которых следует знать.

Изменения между версиями затрагивают не только основной фреймворк, но и экосистему (новый vue-router, state manager и т.д.), а также другие зависимости. В Vue 3 также появился новый официальный сборщик Vite (заменяющий WebPack), новый роутер и управление состоянием (Pinia, замена Vuex), а также другие плагины.

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

Здесь приведен неполный список основных изменений, помимо нового Composition API:

  • Иной способ загрузки и запуска приложения
  • Изменения в регистрации глобальных компонентов и плагинов
  • Изменения в свойстве data
  • Изменения в v-model, props и emits
  • Варианты реактивности
  • Совместимость с браузерами
  • Изменения в организации каталогов и файлов
  • Изменения в маршрутизаторе и управлении состояниями

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

Другой способ загрузки и запуска приложения

Изменился способ загрузки и запуска нашего приложения. Теперь требуется импортировать конструктор из пакета Vue. Давайте сравним обе реализации из main.js:

Vue 2 инстанцирование приложения:

js
import Vue from "vue"
const app = new Vue({el: "#app"})

В Vue 2 мы импортируем конструктор Vue и передаем объект с опциями. В Vue 3 после создания приложения мы подключаем компоненты, плагины и т.д., а затем монтируем наше приложение к компоненту верхнего уровня. Вот пример, переписанный для Vue 3:

Vue 3 инстанцирование приложения:

js
import {createApp} from "vue"
const app = createApp({..})
app.mount("#app")

Положение файла index.html также изменилось, и теперь он располагается в корне нашего приложения. Более подробно изменения в структуре документа описаны в главе 3, Установка рабочего проекта.

Регистрация глобальных компонентов, плагинов и так далее

В Vue 2 мы объявляем компонент для всего приложения (глобальный), прикрепляя его к корневому экземпляру Vue. Вот пример:

js
import Vue from "vue"
import MyComponent from "MyComponent.vue"
vue.component( "myComponent", MyComponent)
const app = new Vue({...})

В Vue 3 вместо этого мы регистрируем компоненты и плагины в приложении после его создания и до его монтирования. Методы component (для компонентов), use (для плагинов) и directive (для директив) являются цепочечными. Вот как предыдущий пример выглядит в Vue 3:

js
import { createApp }from "vue"
import MyComponent from "MyComponent.vue"
const App = createApp({...})
App.component( "myComponent", MyComponent).mount( "#app")

Если нам не нужно ссылаться на приложение, мы можем просто конкатенировать инстанцию приложения, как в данном примере:

js
import { createApp }from "vue"
import MyComponent from "MyComponent.vue"
createApp({...}).component("myComponent", MyComponent) .mount("#app")

Загрузка приложения не зависит от синтаксиса, используемого для описания компонентов (Options API, Composition API или настройка сценария).

Свойство data теперь всегда является функцией

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

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

Приведем пример корневого компонента в main.js:

js
createApp({
  data() {return {...}}
})

А затем во всех остальных компонентах выполняется следующее:

js
export default {
  data() {return {...}}
}

Обратите внимание, что для наглядности в этих примерах мы используем API Options. При использовании синтаксиса <script setup> объявлять атрибут data не требуется.

Больше реактивных возможностей

При использовании Composition API у нас есть два варианта создания реактивных свойств: ref() и reactive(). Первая возвращает объект со свойством .value, которое является реактивным. Вторая преобразует объект, переданный в качестве аргумента, и возвращает тот же объект с реактивными свойствами. Вот один из примеров:

vue
<script setup>
import {reactive, ref} from "vue"
const
    data = reactive({name: "John", surname: "Doe"}),
    person = ref({name: "Jane", surname: "Doe"})
     // Затем, для доступа к значениям на JavaScript
     // Реактивный объект
    data.name = "Mary"
    data.surname = "Sue"
     // Реактивная ссылка
    person.value.name = "Emma"
    person.value.surname = "Smith"
</script>
<template>
    <strong>{{data.surname}}, {{data.name}}</strong><br>
    <strong>{{person.surname}}, {{person.name}}</strong>
</template>

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

ref()reactive()
- Применяется к любому типу данных, а не только к примитивам.
- При применении к объектам или массивам вы можете заменить их.
- Он использует методы получения и установки для обнаружения изменений и запуска реактивной реакции.
- Используйте его по умолчанию для простых данных. Для массивов и объектов (сложных типов) рекомендуется использовать reactive() при работе с их внутренними элементами. Когда будет заменен весь объект, удобно использовать ref().
- Применяется к объектам и массивам, но не к примитивам. Делает их атрибуты реактивными.
- Объект не может быть заменен, только его атрибуты.
- Он использует нативную реализацию обработчиков Proxy() для обнаружения изменений и запуска реактивности.
- Используйте когда вам нужно сгруппировать большое количество переменных, которые должны «путешествовать» вместе.

Таблица А.1 - Простое руководство по выбору между ref() и reactive().

Каждый метод имеет свои преимущества. С точки зрения реактивных свойств со сложными типами не имеет значения, какой из них использовать. В некоторых случаях reactive() может быть более производительным за счет использования нативных реализаций в браузере.

Изменения в v-model, props и events

Это большое изменение по сравнению с Vue 2, которое может привести к поломке вашего кода. В Vue 3 мы больше не получаем и не испускаем значение свойства. Вместо этого любой props может быть входным/выходным, например v-model. По умолчанию атрибут v-model принимается в prop с именем modelValue, а аналог emit добавляет update:, поэтому он называется update:modelValue.

В Vue 3 появилась возможность одновременного использования нескольких v-моделей. Например, мы можем иметь v-model:person="person" в нашем компоненте, определить prop как "modelPerson" и событие как "update:modelPerson".

Props и emits теперь являются макросами (макрос - это специальная функция, предоставляемая сборщиком или фреймворком). Props имеют тот же смысл, что и в Vue 2, поэтому их можно определять как массивы, объекты, включаемые типы, значения по умолчанию и т. д.

Приведем пример с v-моделью по умолчанию и нотированной моделью:

js
const $props = defineProps(['modelValue','modelPerson']),
$emit = defineEmits(['update:modelValue','update:modelPerson'])

Более подробно реквизиты и эмиты рассматриваются в этой книге в главе 4, Композиция пользовательского интерфейса с компонентами.

Удалена совместимость со старыми браузерами

Vue 3 был создан для скорости и "современного" JavaScript. Обратная совместимость со старыми браузерами была удалена. Многие внутренние функции, используемые для обеспечения реактивности, теперь по умолчанию используют нативные реализации (например, Proxy API).

Если вам необходимо поддерживать приложение в устаревшем браузере, то лучше остановиться на Vue 2, но не стоит бояться! Для Vue 2 существует официальный плагин для использования нового Composition API, включающий script setup синтаксис:

  • Vue 2.7 включает его без плагинов (https://blog.vuejs.org/posts/vue-2-7-naruto.html )
  • Если вы используете Vue 2.6 или ниже, вы можете найти плагин здесь: https://github.com/vuejs/composition-api
  • Если вам все же нужна скорость Vue 3, существует специальная сборка для миграции, которая имеет практически тот же API, что и Vue 2 (см. https://v3-migration.vuejs.org/migration-build.html )
  • Зачем удалять совместимость со старыми браузерами? На это есть много причин, в том числе следующие:
    • В мире доля использования старых браузеров упала до значительного уровня, и ожидается, что она продолжит снижаться с течением времени.
    • После удаления старого кода и проверок на совместимость, полученная реализация ядра Vue становится легче и производительнее. Увеличение скорости и уменьшение размера пакета является значительным, что позволяет нашим приложениям загружаться быстрее и быть более отзывчивыми.

На практике большую часть рынка занимают два браузерных движка: браузеры на базе Chromium и браузеры на базе Mozilla Firefox. Проверьте www.caniuse.com, если вам необходимо использовать функцию, которая может быть недоступна в старых браузерах.

Изменения в организации каталогов и файлов

На организацию структуры каталогов в Vue 2 в некоторой степени повлияли тогдашний сборщик WebPack и Vue CLI. Теперь, когда Vue 3 перешел на Vite, файлы были организованы таким образом, чтобы лучше отражать рабочий процесс разработки.

Так, например, index.html переместился в корневую папку из папки Public/. Теперь он занимает более заметное место в процессе компоновки. Эти и другие изменения описаны в главе 3, Установка рабочего проекта.

Изменения в роутере и state manager

Новый подход к компонентам и модульности также затрагивает маршрутизатор и управление состоянием. В то время как для Vue 3 была разработана новая версия маршрутизатора, официальное решение для управления состоянием перешло от Vuex к Pinia. Более подробную информацию о новом маршрутизаторе и Pinia можно найти в главе 5, Одностраничные приложения, и в главе 7, Управление потоком данных, соответственно.

В новом маршрутизаторе реализован иной подход к определению режимов, используются конструкторы createWebHashHistory (режим хэша), createWebHistory (режим истории) и createMemoryHistory (навигация только в памяти).

Это изменение также затронуло конфигурацию собираемого пакета. В WebPack в режиме истории путь развертывания был частью конфигурации сборщика. Теперь путь передается в конструктор в качестве параметра и полностью обрабатывается маршрутизатором.

Новые компоненты и другие изменения.

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

Другие изменения

Полный список изменений, не упомянутых здесь, можно найти в официальной документации по адресу https://v3-migration.vuejs.org/breaking-changes/.

Итог

Переход с Vue 2 на Vue 3 имеет четкую траекторию, с небольшим количеством изменений, о которых следует знать. Новый API Composition, однако, требует изменения менталитета, но это происходит естественно при использовании синтаксиса script setup.

Но самая важная особенность Vue 3 - это рост производительности и уменьшение размера. Одним словом, Vue 3 работает быстро, очень быстро, и переход на него стоит того.

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