В июне 2021 года команда Vue.js выпустила версию Vue 3.1. Этот новый релиз идет вместе с @vue/compat (он же «сборка миграции»). Он позволяет плавно переносить большие проекты, запуская кодовую базу Vue 2 вместе с изменениями Vue 3.
Переход на Vue 3 может быть сложной задачей (в зависимости от размера вашего проекта). В Crisp мы недавно перенесли наше приложение (250 тыс. строк кода) с Vue 2.6 на Vue 3.2 примерно за 2 недели.
Мы готовились к этой миграции, изучая официальное руководство по миграции Vue, но при миграции проекта столкнулись со множеством различных проблем.
Поэтому сочли, что, было бы хорошо поделиться своими знаниями, чтобы другие компании могли извлечь пользу из нашего опыта. Эта статья расскажет вам о:
Фреймворк VUE JS: быстрый старт
Получите бесплатный курс и узнайте, как создать веб-приложение на трендовой Frontend-технологии VUE JS с полного нуля
Основных различиях между Vue 2 и Vue 3
Дилеммах, с которыми мы столкнулись, и как мы с ними справлялись
Нашей стратегии миграции на Vue.js
Немного предыстории Vue 3
Содержание статьи:
Первоначальная философия Vue.js, созданного в 2013 году, заключалась в создании минималистичной альтернативы AngularJS.
В то время Angular представлял собой громоздкий фреймворк с массой новых функций. Первые версии Vue.js были похожи на мини-фреймворк с шаблонами, привязкой данных, фильтрами и директивами.
Затем, в 2016 году, был выпущен Vue 2 (почти одновременно с Angular 2) и стал отличной альтернативой AngularJS. Фактически, многие разработчики, использующие Angular 1, решили перейти на Vue 2, потому что им не нравился Angular 2: Vue 2 предлагал столь же простые решения, как и AngularJS, но с лучшей производительностью.
Vue 3 создавался с расчетом на производительность. Это скорее эволюция, чем революция. Большинство новых функций и критических изменений были выпущены из соображений производительности.
Внутренняя система реактивности была переработана с нуля, и по этой причине была прекращена поддержка IE 11 (что не является большой потерей). Наконец, новая версия является более модульной и включает такие функции, как Composition API.
Самые большие изменения в Vue 3
Просмотр глобального API
Глобальный API Vue устарел. Хотя он все еще поддерживается, потребуется переработать ваше приложение, чтобы обеспечить поддержку Vue 3.
Это означает, что невозможно будет использовать такие API, как Vue.set или Vue.delete. Поскольку Vue 3 поставляется с новой системой реактивности, использование этих API становится бесполезным.
Вместо использования вам придется напрямую использовать . То же самое с , которое можно заменить на .
Фильтры
Как объяснялось ранее, Vue.js был создан как альтернатива Angular 1. Именно поэтому изначально поддерживались фильтры.
Самая большая проблема с фильтрами — это производительность: функция фильтрации должна выполняться каждый раз, когда данные обновляются. По этой причине поддержка фильтров в Vue 3 была прекращена. Это означает, что невозможно будет использовать такие вещи, как в шаблонах Vue 3.
Вместо этого вам придется использовать вычисляемые свойства, такие как или методы, например .
Создание экземпляров Vue и плагинов
Начиная с Vue 3, ваше приложение и плагины больше не создаются глобально. Это означает, что в одном проекте может быть несколько приложений Vue. Использование Vue 2:
JavaScript import Vue from “vue”; new Vue({ router, render: h => h(App) }).$mount(“#app”);
123456 | import Vue from “vue”; new Vue({ router, render: h => h(App)}).$mount(“#app”); |
Использование Vue 3:
JavaScript import { createApp, h } from “vue”; const app = createApp({ render: () => h(App) }); app.use(router); app.mount(“#app”);
123456789 | import { createApp, h } from “vue”; const app = createApp({ render: () => h(App)}); app.use(router); app.mount(“#app”); |
v-if + v-for
Использование v-if условий со v-for списками раньше было возможно в Vue 2. По соображениям производительности эта возможность была отключена в Vue 3. Начиная с Vue 3, вам придется использовать свойства вычисляемого списка.
v-model
V-model API изменилась в Vue 3. Во-первых – свойство value была переименовано в modelValue.
JavaScript <ChildComponent v-model=”pageTitle” />
1 | <ChildComponent v-model=”pageTitle” /> |
ChildComponent нужно переписать так:
JavaScript props: { modelValue: String // previously was `value: String` }, emits: [‘update:modelValue’], methods: { changePageTitle(title) { this.$emit(‘update:modelValue’, title) } }
1234567891011 | props: { modelValue: String // previously was `value: String`}, emits: [‘update:modelValue’], methods: { changePageTitle(title) { this.$emit(‘update:modelValue’, title) }} |
Круто то, что теперь можно иметь несколько пользовательских значений v-model, например v-model:valueA, v-model:valueB и т. д.
$ emit
В Vue 2 можно было использовать экземпляр Vue для создания глобального EventBus с помощью vm.$on и vm.$off. Начиная с Vue 3, это больше невозможно, поскольку vm.$on и vm.$off были удалены из экземпляра Vue. В качестве замены предлагаем использовать библиотеку Mitt:
JavaScript mounted() { this.eventbus = mitt(); eventbus.on(“ready”, () = { console.log(“Event received”); }); eventbus.emit(“ready”); }
123456789 | mounted() { this.eventbus = mitt(); eventbus.on(“ready”, () = { console.log(“Event received”); }); eventbus.emit(“ready”);} |
Тот же код, использующий Vue 2, будет:
JavaScript mounted() { this.$on(“ready”, () = { console.log(“Event received”); }); this.$emit(“ready”); }
1234567 | mounted() { this.$on(“ready”, () = { console.log(“Event received”); }); this.$emit(“ready”);} |
По-прежнему можно отправлять события из компонентов в их родительские компоненты, однако все события должны быть объявлены с помощью новой опции emit (она очень похожа на существующую props).
Например, если у вашего компонента есть свойство @click, полученное с помощью this.$emit(«click»), вам нужно будет объявить событие «click» в своем компоненте:
JavaScript props: { name: { type: String, default: “” }, }, emits: [“click”], // events have to be declared here data() { return { value: “” } }
1234567891011121314 | props: { name: { type: String, default: “” },}, emits: [“click”], // events have to be declared here data() { return { value: “” }} |
Стратегия миграции
Наше приложение называется Crisp. Это приложение для обмена сообщениями для крупного бизнеса, которым ежедневно пользуются 300 тысяч компаний по всему миру. Компании используют Crisp, чтобы отвечать своим клиентам, используя папку «Входящие», приложение централизует чат, электронную почту и многие другие каналы.
Поскольку наше текущее приложение используется многими разными пользователями, очевидно, важно было ничего не сломать. Нам также нужно было ежедневно улучшать программное обеспечение, чтобы мы могли исправлять ошибки и новые функции. Итак, нам нужно было иметь две разные ветки:
основная ветка для Vue 2.6 с текущим жизненным циклом релиза
ветку Vue3, чтобы работать над миграцией
Фреймворк VUE JS: быстрый старт, первые результаты
Получите бесплатный курс и узнайте, как создать веб-приложение на трендовой Frontend-технологии VUE JS с полного нуля
Кроме того, мы каждый день выпускали бета-версию нашей кодовой базы Vue 3 и переносили почти повседневные изменения, сделанные в ветке Vue 2, в ветку Vue 3, чтобы избежать головной боли после слияния с кодовой базой Vue 3.
Наконец, мы решили не полагаться на новые API Vue, такие как Composition API, и перенести только то, что необходимо. Причина этого заключалась в том, чтобы снизить риск введения регрессий.
Обновление инструментов сборки Vue
Наше приложение использует Vue Cli (веб-пакет). Опять же, мы решили пока не переходить на Vite, чтобы не допустить появления новых проблем, поскольку наша система сборки довольно сложна. Перенести Vue Cli на поддержку Vue 3 довольно просто.
Сначала нужно отредактировать файл package.json, чтобы обновить Vue.js и его зависимости. Поэтому, заменим на .
Кроме того, нам придется использовать , что позволит плавно перенести кодовую базу с Vue 2 на Vue 3.
Нам также придется обновить на . Теперь нам нужно обновить наш файл vue.config.js и отредактировать функцию chainWebpack. Это заставит все ваши существующие библиотеки использовать пакет @vue/compat.
JavaScript // Vue 2 > Vue 3 compatibility mode config.resolve.alias.set(“vue”, “@vue/compat”); config.module .rule(“vue”) .use(“vue-loader”) .loader(“vue-loader”) .tap(options => { // Vue 2 > Vue 3 compatibility mode return { …options, compilerOptions: { compatConfig: { // default everything to Vue 2 behavior MODE: 2 } } }; });
12345678910111213141516171819 | // Vue 2 > Vue 3 compatibility mode config.resolve.alias.set(“vue”, “@vue/compat”); config.module .rule(“vue”) .use(“vue-loader”) .loader(“vue-loader”) .tap(options => { // Vue 2 > Vue 3 compatibility mode return { …options, compilerOptions: { compatConfig: { // default everything to Vue 2 behavior MODE: 2 } } }; }); |
Обновление нашего файла main.js
Теперь, нам нужно создать экземпляр Vue следующим образом:
JavaScript import { createApp, h, configureCompat } from “vue”; const app = createApp({ render: () => h(App) }); app.use(router); app.use(store); // Initiate other plugins here configureCompat({ // default everything to Vue 2 behavior MODE: 2 }); app.mount(“#app”);
12345678910111213141516 | import { createApp, h, configureCompat } from “vue”; const app = createApp({ render: () => h(App)}); app.use(router);app.use(store);// Initiate other plugins here configureCompat({ // default everything to Vue 2 behavior MODE: 2}); app.mount(“#app”); |
Обновление Vue Router
Нам нужно будет использовать последнюю версию Vue Router . Использование последней версии маршрутизатора Vue.js не сильно отличается. Основное отличие состоит в том, что придется вручную включить режим истории, используя:
JavaScript import { createWebHistory, createRouter } from “vue-router”; var router = createRouter({ history: createWebHistory(), routes: [ // all your routes ] });
12345678 | import { createWebHistory, createRouter } from “vue-router”; var router = createRouter({ history: createWebHistory(), routes: [ // all your routes ]}); |
Перенос фильтров
Наше существующее приложение во многом зависит от фильтров (около 200 использований). Первым делом нужно было выяснить, сколько фильтров мы использовали. Поскольку современные IDE (VSCode, SublimeText) поддерживают поиск Regex, мы создали регулярное выражение, чтобы мы могли узнать все использования фильтров Vue в наших шаблонах:
Поскольку Vue 3 полностью отказывается от поддержки фильтров, мы попытались найти элегантный способ их использования. Решение заключалось в переносе фильтров Vue на пользовательские Singletons Helpers. Например, на Vue 2:
JavaScript Vue.filter(“uppercase”, function(string) { return string.toUpperCase(); });
123 | Vue.filter(“uppercase”, function(string) { return string.toUpperCase();}); |
Становится на Vue 3:
JavaScript uppercase(string) { return string.toUpperCase(); } export { uppercase };
12345 | uppercase(string) { return string.toUpperCase();} export { uppercase }; |
Затем мы глобально создали экземпляр класса StringsFilter:
JavaScript import { uppercase } from “@/filters/strings”; app.config.globalProperties.$filters = { uppercase: uppercase };
12345 | import { uppercase } from “@/filters/strings”; app.config.globalProperties.$filters = { uppercase: uppercase}; |
Наконец, мы можем использовать наш фильтр: становится
Исправление ошибок
По мере продвижения процесса миграции вы будете замечать ошибки в консоли браузера. В режиме совместимости с Vue 3 предусмотрены различные журналы, которые помогут вам перенести приложение на Vue 3.
Вместо того, чтобы пытаться переносить от функции к функции или от шаблона к шаблону, мы рекомендуем переносить от API к API.
Например, если вы видите в журналах уведомление о том, что WATCH_ARRAY устарел, мы рекомендуем ознакомиться с руководством по миграции, указанным в журналах. После завершения миграции всех массивов вы можете отключить режим совместимости для этой функции:
JavaScript import { createApp, h, configureCompat } from “vue”; const app = createApp({ render: () => h(App) }); // Initiate all plugins here configureCompat({ // default everything to Vue 2 behavior MODE: 2, // opt-in to Vue 3 behavior for non-compiler features WATCH_ARRAY: false }); app.mount(“#app”);
1234567891011121314151617 | import { createApp, h, configureCompat } from “vue”; const app = createApp({ render: () => h(App)}); // Initiate all plugins here configureCompat({ // default everything to Vue 2 behavior MODE: 2, // opt-in to Vue 3 behavior for non-compiler features WATCH_ARRAY: false}); app.mount(“#app”); |
Обновление библиотек
Vue 3 поставляется с пакетом @vue/compat, поддерживающим библиотеки Vue 2 и Vue 3. Однако режим @vue/compat снижает производительность.
Использование режима совместимости возможно только при преобразовании вашего приложения и всех его библиотек в Vue 3. После того, как весь проект и библиотеки будут преобразованы, вы можете избавиться от режима совместимости.
Чтобы этого добиться, нам придется перенести все библиотеки на совместимые с Vue 3. Все официальные библиотеки Vue (Vuex, Vue Router) были перенесены на Vue 3, в то время как большинство популярных пакетов уже имеют версии, совместимые с Vue 3.
Перенос библиотеки Vue 3 обычно довольно прост и занимает от 30 минут до полдня, в зависимости от сложности библиотеки.
По завершении миграции библиотек для поддержки Vue 3 мы создаем pull request в исходную библиотеку, чтобы другие могли извлечь выгоду из нашей работы.
О производительности
Vue 3 имеет множество различных улучшений производительности благодаря новой внутренней системе реактивности. Размер Javascript heap был уменьшен на 20-30% в большинстве случаев и на 50% для некоторых сложных списков. В нашем случае использования обмена сообщениями мы заметили значительные улучшения приозводительности ЦП. Наше новое приложение Crisp работает быстрее и легче.
Заключение
Пока мы пишем эту статью, мы развертываем новое приложение Crisp в производственной среде. На перенос этого приложения у нас ушло около 2 недель, работали 2 разработчика почти полный рабочий день. Для сравнения, переход с Angular 1 на Vue 2 занял у нас около 9 месяцев.
Переход с Vue 2 на Vue 3 — важная, и далеко не невыполнимая задача. Новая версия Vue предлагает несколько отличных улучшений производительности.
Чтобы поблагодарить сообщество Vue.js, мы начали вносить свой вклад в проект в качестве золотого спонсора . Как компания, мы чувствуем, что должны расширять возможности проектов с открытым исходным кодом, чтобы построить лучший мир, в котором software(програмное обеспечение) и environment (окружающая среда ) могут взаимодействовать друг с другом.
Автор: Baptiste Jamin
Источник: webformyself.com