Привет! Меня зовут Влад, я frontend-разработчик в компании SimbirSoft. Мне приходилось создавать приложения как на старых версиях Vue, так и на новых. Причем многие из моих коллег вполне успешно разрабатывают на Vue 2 и не спешат переходить на Vue3, даже спустя два года после релиза.
Что же касается бизнеса и владельцев продуктов, в моей практике также встречались кейсы и примеры, когда заказчики не понимали всех преимуществ использования новой версии.
В этой статье попытался раскрыть новшества, которые могут стать «триггером» для миграции на новую технологию для обеих заинтересованных групп. Поговорим об экосистеме Vue 3, о новинках и пользе для разработчиков и бизнеса. И, разумеется, сравним Vue 2 и Vue 3 с технической точки зрения. Также рассмотрим одно из главных нововведений фреймворка – Composition API, раскроем технические нюансы и определим лучшие кейсы использования нового API.
История появления Vue
Vue – это Javascript-фреймворк для разработки пользовательских интерфейсов.
Создателем и главным разработчиком фреймворка является бывший работник Google Эван Ю. Первый коммит Vue был сделан в июне 2013 года и назывался Seed.js. Но, как выяснилось, имя seed было уже занято в npm, и название изменили на Vue.
Эван изначально писал фреймворк, который понравился бы ему как разработчику. Но первые несколько сотен звезд на GitHub мотивировали его создать продукт, который будет приятно и эффективно использовать.
Сейчас Vue – это крупнейший проект, который пользуется колоссальной финансовой и технической поддержкой партнёров и разработчиков из всего мира. В итоге то, что изначально задумывалось как инструмент для внутреннего пользования компании, переросло в один из лучших фреймворков для JavaScript.
Преимущества Vue 3
Концепция и основная идея новой версии Vue 3 возникли в 2018 году. На текущий момент времени с момента выхода Vue 3 версии прошло почти два года (дата релиза – сентябрь 2020 года).
Она включает в себя масштабные изменения в исходном коде и новый API. В ней также значительно улучшена производительность – в среднем 1,5-2 раза быстрее прошлой. Исходный код фреймворка был переписан с нуля, а при помощи использования TypeScript изменена система реактивности. А Vetur, официальное расширение VSCode, теперь поддерживает проверку типов у шаблона и свойств с использованием улучшенной внутренней типизации Vue 3.
Ниже раскроем основные преимущества 3-й версии, а также расскажем, почему новая версия может быть интересна не только разработчикам, которые получают существенные нововведения в части измененного функционала и улучшений производительности, но и владельцам бизнеса, которые выбирают Vue для создания своих продуктов.
Преимущества для разработчиков
Улучшенная интеграция с TypeScript
Vue 3 был полностью переписан на TypeScript и на текущий момент обеспечивает полноценную поддержку TypeScript. Все официальные пакеты Vue 3 поставляются со встроенными объявлениями типов, которые должны работать «из коробки».
А Composition API хорошо работает с типизацией данных в приложении.
Новые API для крупных приложений
Vue 3 предоставляет совершенно новый API Composition API - это мощнейшее решение, которое в полной мере разрешает проблемы Options API Vue 2 версии, а также предоставляет расширенные возможности в декларировании и управлении данными в фреймворке. Новый API позволяет облегчить реализацию сложных архитектурных решений в крупных и средних приложениях, по средствам обеспечения логической связанности и возможности повторного использования, похожую на хуки в React.
Порог входа
Vue придерживается консервативного подхода к созданию шаблонов и стилей, которые отделены от логики приложений и используют HTML и CSS. Для многих программистов это более привычно и облегчает постепенную миграцию существующих приложений.
Таким образом, исполнителю нужно меньше опыта, чтобы вносить доработки в текущую кодовую базу на Vue.
Фреймворк также отличает гибкость и модульность – можно моментально приступить к созданию приложения, не тратя лишнее время на конфигурирование c помощью инструмента командной строки VUE CLI.
Архитектура приложений
Vue использует подход ViewModel из шаблона MVVM. Он хорошо работает для более крупных приложений, используя двустороннюю привязку данных. Основная цель Vue – дать простой и гибкий уровень представления, а не полноценный фреймворк.
Документация
Разработчики Vue здесь очень постарались. Есть сайт с документацией, подробным описанием на нескольких языках и ответами сообщества на разные вопросы. А руководство и справку по методам API иногда даже признают лучшими в отрасли.
Остальные преимущества:
Новая концепция реактивности.
Улучшенная оптимизация и производительность.
Composition API.
Практически полная обратная совместимость с API второй версии.
Паттерн для повторного использования логики компонентов.
Преимущества для бизнеса
Безопасность
Хотя автоматически защитить приложения от XSS и других уязвимостей невозможно, разработчики Vue могут санитизировать HTML-код перед реализацией или использовать внешние библиотеки для защиты от атак. В тех случаях, когда вы знаете, что HTML безопасен, можно явно отображать содержимое HTML и защищать приложение до и после рендеринга. Исходя из последних статистических данных, следует отметить, что последняя уязвимость была обнаружена во второй версии Vue
Улучшение производительности
Vue 3 показывает значительные улучшения производительности. Если вы работаете с продуктом, который требует быстрого обновления содержимого и контента, выбор очевиден – это Vue 3. Новая версия имеет следующие показатели:
Поддержка
В будущем разработчики Vue, скорее всего, прекратят поддержку версии 2.x. Выбирая Vue 3, можно получить поддерживаемый и улучшаемый инструмент на годы вперёд.
Остальные преимущества Vue 3:
Улучшенная адаптация под большие и средние проекты.
Ориентация на современные вспомогательные инструменты, в частности, улучшена поддержка TypeScript.
Новые функциональные возможности для решения бизнес-задач.
Улучшение процессов разработки и отладки продукта на всех этапах.
Ключевые функциональные преимущества Vue 3
В этом разделе рассмотрим самые интересные функциональные улучшения новой версии Vue.
Изменённые алгоритмы сравнения виртуального DOM
Процесс изменения реального DOM дерева в Vue построен на вызове функции, которая сравнивает два объекта Vnode (oldNode, vNode), и последующем патчинге изменений в DOM браузера. В Vue 2 такой алгоритм был построен на сравнении всех узлов без фокуса на тип узла.
Концептуальная особенность алгоритма сравнения Vue 3 – сравниваются только Vnode элементы с динамической привязкой, а статические игнорируются.
Рассмотрим следующий пример:
<div> <p> Привет, {{username}} </p> <p> Приятно с тобой познакомиться </p>
<p> Время уже позднее, увидимся завтра </p></div>
Как видите, при вызове функции сравнения будет анализироваться лишь первый абзац, а остальные два статических элемента попросту будут проигнорированы. Vue 3 запоминает статический контент, чего Vue 2 делать не мог.
Статический подъем
Для лучшего понимания концепции статического подъема рассмотрим однородную структуру шаблона компонента на следующем примере:
<div id="app">
<div>
<div>SimbirSoft — компания по разработке программного обеспечения.</div>
<div>{{ contentDynamic }}</div>
</div>
</div>
А теперь посмотрим на render-функцию для версий Vue 2 и Vue 3
Vue 2
function render() {
var vm = this;
var _h = _vm.$createElement;
var _c = _vm._self._c || _h;
return _c('div', {
attrs: {
"id": "app"
}
}, [_c('div', [_c('div', [_vm._v(" " + _vm._s(_vm.msg) + " ")]), _c('div',
[_vm._v(
" SimbirSoft -- компания по разработке программного обеспечения. "
)])])])
}
Во Vue 2 независимо от того, является ли элемент статическим, он повторно создается в render-функции. Затем рендерится в шаблоне, задействуя при этом больше ресурсов.
Vue 3
import { createElementVNode as _createElementVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
const _hoisted_1 = { id: "app" }
const _hoisted_2 = /*#__PURE__*/_createElementVNode("div", null, "SimbirSoft — компания по разработке программного обеспечения.", -1 /* HOISTED */)
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock("div", _hoisted_1, [
_createElementVNode("div", null, [
_hoisted_2,
_createElementVNode("div", null, _toDisplayString(_ctx.dynamic), 1 /* TEXT */)
])
]))
}
Во Vue 3 все статические элементы, которые не участвуют в динамическом изменении данных, поднимаются вверх, позволяя избежать повторного пересоздания, что, как следствие, экономит ресурсы.
Новая концепция реактивности
Реактивность – это механизм декларативного обновления данных в шаблоне, которые объявлены в коде. Во Vue 2 такой механизм строился на «геттерах» и «сеттерах» путём переопределения свойств объекта в инстансе Vue c помощью Object.defineProperty.
Во Vue 3 концепция реактивности полностью построена на Proxy. Это объект, который содержит в себе другой объект или функцию и позволяет «перехватывать» их.
Proxy во Vue 3 решает некоторые проблемы, присущие системе реактивности Vue 2. Например, добавление новых реактивных свойств в объект либо слежение за всем объектом, а не за единичным свойством. Подробнее про реактивность в новой версии можно прочитать здесь.
Конечно, в целом, концептуальных изменений гораздо больше. По ссылкам ниже можно найти еще несколько дополнительных интересных особенностей:
А мы перейдем к рассмотрению одного из главных нововведений Vue 3.
Composition API
Composition API – это совершенно новое решение организации компонентов в Vue 3. Оно представляет собой набор API:
API реактивности – сущности ref и reactive.
Методы для взаимодействия с жизненным циклом компонента, onMounted, onUpdated и другие.
Внедрение зависимостей provide/inject.
При первом знакомстве с технологией может появиться вопрос, зачем это нужно, если есть старый и добрый options API. Попробуем разобраться, какие проблемы призван решить новый API.
Улучшенная организация кода в компонентах
Рассмотрим структуру двух совершенно одинаковых компонентов для взаимодействия с постами с точки зрения использования разных API.
Options API
<script>
import ***axios*** from 'axios';
export default {
data() {
return {
posts: [],
authorPosts: [],
favoritePost: null,
}
},
mounted() {
this.fetchPosts();
},
methods: {
async fetchPosts() {
this.posts = (await axios.get('https://jsonplaceholder.typicode.com/posts')).data;
},
async getPostByAuthor(id) {
this.authorPosts = (await axios.get(`https://jsonplaceholder.typicode.com/posts?userId=${id}`)).data;
},
async getCommentsByPost() {
this.comments = (await axios.get('https://jsonplaceholder.typicode.com/posts/1/comments')).data;
},
addPostToFavorite(id) {
this.favoritePost = this.posts.find(post => post.id === id);
},
},
computed: {
getFavoritePostId() {
return this.favoritePost?.id;
},
}
}
</script>
Composition API
<script>
import {ref, computed} from 'vue';
import axios from 'axios';
export default {
setup() {
/* region posts */
const posts = ref([]);
(async () => {
posts.value = (await axios.get('https://jsonplaceholder.typicode.com/posts')).data;
})();
const authorPosts = ref([]);
const getPostByAuthor = async (id) => {
authorPosts.value = (await axios.get(`https://jsonplaceholder.typicode.com/posts?userId=${id}`)).data;
};
/* endregion */
/* region favoritePosts */
const favoritePost = ref(null);
const addPostToFavorite = (id) => {
favoritePost.value = posts.value.find(post => post?.id === id);
}
const getFavoritePostId = computed(() => {
return favoritePost.value?.id;
})
/* endregion */
/* region comments */
const comments = ref([]);
const getCommentsByPost = async (postId) => {
comments.value = (await axios.get(`https://jsonplaceholder.typicode.com/posts/${postId}/comments`)).data;
};
/* endregion */
// other logic
// other logic
// region return
return {
posts,
comments,
addPostToFavorite,
getPostByAuthor,
getFavoritePostId,
getCommentsByPost,
}
// endregion
}
}
</script>
Как мы видим, в случае с composition API, структурировать код по логическому назначению легче. Это позволяет сохранять читаемость и масштабируемость компонента.
Декомпозиция и повторное использование кода
Методы, которые предоставляет нам Vue 2 для решения проблем декомпозиции и переиспользования кода в больших компонентах, иногда приводят к побочным эффектам. Например, Mixins может перезатереть уже существующий функционал, а Provide Inject, исходя из документации, рекомендовано использовать не во всех случаях. Кроме того, по умолчанию данные, монтируемые с помощью provide/inject, не реактивны.
С новым API мы можем инкапсулировать логику взаимодействия с постами и комментарии отдельно, а также переиспользовать в любом компоненте с помощью хука. Рассмотрим на примере.
Для наглядности упростим компонент до минимального функционала: получение и добавление поста в избранное. Логику вынесем за пределы компонента.
<script>
import { onMounted } from 'vue';
import usePosts from '@/logic/posts';
export default {
setup() {
/* region posts */
const { fetchPosts, addPostToFavorite, favoritePost, posts } = usePosts();
onMounted(async () => {
await fetchPosts();
})
/* endregion */
// region return
return {
addPostToFavorite,
favoritePost,
posts,
}
// endregion
}
}
</script>
Наш внешний файл с логикой
import { ref } from 'vue';
import axios from 'axios';
const posts = ref([]);
const favoritePost = ref(null);
export default function usePosts () {
const fetchPosts = async () => {
posts.value = (await axios.get('https://jsonplaceholder.typicode.com/posts')).data;
}
const addPostToFavorite = (id) => {
favoritePost.value = posts.value.find(post => post?.id === id);
}
return {
posts,
favoritePost,
addPostToFavorite,
fetchPosts,
}
}
Как мы видим, с помощью новых возможностей легко отделить логику в внешний файл и использовать, где потребуется. Поле для масштабируемости данного подхода огромно. Вплоть до отказа от всеми известного менеджера состояний Vuex. С помощью данного подхода мы можем организовывать свой store без использования сторонних библиотек. Тем, кто любит архитектурные решения, рекомендуем взглянуть на npm-пакет Pinia.
А благодаря синтаксическому сахару в виде дополнительного атрибута "setup" в тэге <script> (доступно с версии 3.2), данные в компоненте можно определять еще проще.
<script setup>
import { onMounted } from 'vue';
import usePosts from '@/logic/posts';
const { fetchPosts, addPostToFavorite, favoritePost, posts } = usePosts();
onMounted(async () => {
await fetchPosts();
})
</script>
Магия! Не правда ли?
Улучшенная поддержка TypeScript
В старой версии для внедрения поддержки TypeScript необходимо было использовать классовые компоненты и декораторы. Это не всегда удобно и в целом усложняет процесс разработки.
С появлением Composition API всё изменилось. Новый синтаксис позволяет использовать типизацию с минимальными трудозатратами. Рассмотрим на примерах.
ref
import { ref } from 'vue'
import type { Ref } from 'vue'
const year: Ref<string | number> = ref('2020')
year.value = 2020 // отлично
reactive
import { reactive } from 'vue'
interface Post {
id: number;
title: string;
}
const post: Post = reactive({ id: 1222, title: 'Название' })
computed
import { computed } from 'vue'
const postId = computed<number>(() => {
return post.id;
})
Конечно, на примере более сложных сущностей всё может быть не так просто, но это только с первого взгляда. Если вы знакомы с типизацией Vue 2, то поймете, насколько всё стало лучше и удобнее.
Резюме
Для меня как разработчика, преимущественно на фреймворке Vue, новая версия показала себя с хорошей стороны. По сути, это тот же продукт, который не потерял концепцию декларативного инструмента и в то же время решил проблемы прошлых версий. Стало легче работать с большими объемами данных. К тому же, в третьей версии улучшена поддержка типизации TypeScript. Кроме этого, мы получили новые функциональные возможности, которые предоставляют улучшение процесса разработки в целом.
Исторически так сложилось, что Vue 2 версии больше подходил для применения на небольших и средних проектах, мы сами не раз говорили об этом в своих статьях. Однако сейчас, после масштабных нововведений, можно отметить, что Vue 3 подходит и для крупных коммерческих решений.
Если на момент релиза это все еще был новый инструмент со своими подводными камнями, практически отсутствием npm пакетов и фреймворков, адаптированных под новую версию, то сейчас можно с уверенностью сказать, что Vue 3 – оптимальная версия фреймворка на текущий момент.
Надеемся, статья была вам полезна!
Больше кейсов и полезных материалов для владельцев продуктов - в нашем ВК и Telegram.
Комментарии (20)
morr
08.07.2022 10:28+2У меня двойственные впечатления от vue3.
С одной стороны composition api + script setup прекрасны и позволяют писать боле лаконичные компоненты. Попробовав писать реальный код в новом стиле понимаешь, что это удобнее старого подхода.
С другой стороны в реальной практике не редки случаи, когда компоненты содержат сотни строк кода. Когда разработчики фреймворка предлагают запихивать весь этот код в одну мега-большую лапшеообразную setup функцию, я понимаю, что пора переходить на другой фреймворк.
К счастью script setup больше не экспериментальная фича у vue3, но лучше бы в документации и примерах использования кода не писали примеры с синтаксисомexport default { setup() { ... } }
.
sa1exx
08.07.2022 11:10+1Мне кажется автор всё таки не понимает почему львинная доля продуктов остается на Vue2, и не переходит на новую версию. Причина, по моему субьективному мнению, в том что на переход с версии 2 на версию 3 понадобятся большое, неподьемное для бизнеса, количество человеко-часов. Я бы сравнил переход с Vue2 на Vue3 с переходом с Angular.js на Angular2, казалось бы, один и тот же фреймворк(нет), в чём проблема потратить тысячу сторипоинтов на это? Статья несёт более рекламный подход(интересно зачем?), нежели обьективный, который можно было бы предьявить клиентам.
morr
08.07.2022 11:53+2Не обязательно переписывать весь работающий код на новый синтаксис при апгрейде. Со старым синтаксисом продолжит работать 99% кода, а оставшийся 1% это обновления сторонних vue2 пакетов и шаблонные замены/доработки.
Сложность обновления даже не сильно коррелирует с размером приложения, практически все изменения шаблонны, обновив несколько крупных компонентов, остальные будут обновляться поиском и заменой.pfffffffffffff
08.07.2022 22:43Основная проблема обновления это несовместимость библиотек/компонентов заточенных под 2 версию
nin-jin
08.07.2022 12:03Как мы видим, в случае с composition API, структурировать код по логическому назначению легче. Это позволяет сохранять читаемость и масштабируемость компонента.
Да как-то не видим. Навалено всего в кучу. Вот, смотрите как выглядит настоящая структурированность (я использовал $mol_wire, но на Vue3 не сложно сделать аналогичные декораторы):
import { $mol_wire_solo as solo, $mol_wire_plex as plex, $mol_wire_method as task } from 'mol_wire_lib' class Posts { @solo list(): Post[] { return fetchJson( 'https://jsonplaceholder.typicode.com/posts' ).data } @solo dict() { return new Map( this.list().map( post => [ post.id, post ] ) ) } @plex listByAuthor( authorId: string ): Post[] { return fetchJSON( `https://jsonplaceholder.typicode.com/posts?userId=${authorId}` ).data } } class Favorite { constructor( readonly posts: ()=> typeof Posts ) {} @solo postId( next = null | string ) { return next } @solo post( next = null as null | Post ) { return this.posts().dict().get( this.postId( next?.id ) ) ?? null } } class Comments { @plex listByPost( postId: string ): Comment[] { return fetchJSON( `https://jsonplaceholder.typicode.com/posts/${postId}/comments` ).data } } class Auth { @solo profile(): Profile { return fetchJSON( `https://jsonplaceholder.typicode.com/users/${postId}` ).data } } class Domain { @solo posts() { return new Posts } @solo favorite() { return new Favorite( ()=> this.posts() ) } @solo comments() { return new Comments } @solo auth() { return new Auth } } class App { @solo domain() { return new Domain } @task putMyFirstPostToFavorite() { const profile = this.domain().auth().profile() const firstPost = this.domain().posts().listByAuthor( profile.id )[0] this.domain().favorite().post( firstPost ) } }
для внедрения поддержки TypeScript необходимо было использовать классовые компоненты и декораторы. Это не всегда удобно и в целом усложняет процесс разработки.
Ну да, ну да, использование классов настолько сложно, что я даже уверен в корректности приведённого выше кода, хотя ни разу его и не запускал.
danilovmy
08.07.2022 23:17+4а стоило б:
class Auth { @solo profile(): Profile { return fetchJSON( `https://jsonplaceholder.typicode.com/users/${postId}` ).data } }
откуда берется
postId
?
TrueRomanus
08.07.2022 15:02Vue использует подход ViewModel из шаблона MVVM
Можете пояснить что это значит? Как подход отпочковался от шаблона и в чем этот подход заключается?
SimbirSoft_frontend Автор
08.07.2022 15:02Подход встроен в шаблон. Vue использует весь паттерн MVVM, но фокусируются в большей степени именно на слое ViewModel, в котором и происходит "магия" объединения между слоями View и Model средствами двух сторонней привязки данных. Автор об этом здесь хотел сказать.
zorn-v
08.07.2022 16:45Для тех кто "боится", вышла версия vue 2.7, которая "почти" как 3 но с ограничениями второй. Например несуществующие свойства в реактивном объекте надо объявлять через (Vue|this).set (ну и удалить это все при переходе на 3ку)
А на самом деле я даже не думал как это удобно даже не думать об этом )Одна из киллер фич (для меня) 3й версии в composition api - script setup. В 2.7 тоже перенесли.
yar3333
08.07.2022 17:46Пишем на Vue 2, но на typescript (с декораторами). По-моему, это мегаудобно. Никто не знает - есть ли такое для Vue 3 (чтобы обычные переменные-члены классы автоматом были data, геттеры были computed, методы класса чтобы сразу в methods попадали)? Или я хочу чего-то необычного?
QtRoS
09.07.2022 14:24Крупные либы вроде Veutify и Quasar уже перешли на Vue 3? По идее они являются мажорными потребителями фич и могут провести dogfooding.
tempick
Я думал, уже почти все перешли на него. Я сам по бэкенду, но иногда для себя использую Vue, если надо что-то наклепать на фронте. И часто пишут с предложением работы на фронтенде - как и на постоянную работу, так и просто заказы на фрилансе - вроде для новых проектов все Vue 3+compostiton api просят использовать. Но, может, просто у меня такая выборка? Кто крутится в этом, расскажете, так оно или нет обстоит в целом?
Aleksandr-JS-Developer
До сих пор есть критическая масса приложений ещё даже на JQuery.
По поему опыту, "новые проекты" в студии/компании - это нечастое явление.
А вот 10 старых + 1 новый - вполне. Эти 10 старых вполне могут быть кто на JQuery, кто на vue 2.[5/6].
Поэтому под слоганом "все новые проекты делаем при помощи технологии Х" кроется мааааааленькая деталь, что новый проект будет раз в пару месяцев, а старые никто не отменял.
Это на Хабре все уже во всю гоцают на (Vue3/React)+TypeScript+(куча классных, модных и новых технологий для бекенда).
А возьмите Wappalyzer и пройдитесь по своим самым используемым сайтам и увидите, что не всё так однозначно.
В основном CMS'ки да React с Vue (r/v = ~3-5/1) в лучшем случае.
Редко попадается Angular. Ещё реже что-то прям огненное, типа Nuxt, Next или Quasar.
На свеженьким Svelte ещё ничего замечено мною не было, хотя технология прелюбопытнейшая.
Всё это из-за "зачем тратить человекочасы для перехода, если всё работает?".
tempick
Не, это я прекрасно понимаю. Я говорил именно про "Vue2/Vue3", потому что остальные фреймворки/библиотеки не рассматриваю при работе в принципе.
Кстати, именно Quasar понравился, первую версию использовал для личных целей с большим удовольствием, но ни одной вакансии с ним не предлагали( В основном, везде Vue+vuetify
Про реакты/ангулары и прочее вообще не знаю, как ситуация обстоит да и не особо интересно. Пробовал писать vk mini app на реакте - как-то очень тяжело давалось и некомфортно работать с ним было, код казался каким-то уродливым; больше не пытался
Aleksandr-JS-Developer
Насколько я знаю, сейчас и ближайшее время править будет Vue 2. Особенно в крупных и средних компаниях. Именно из-за "зачем, если всё и так работает".
По причине выше. Со временем появятся, но через сколько - непонятно.
React часто ругают за излишний "обязательный" код.
Сам пишу на Vue и действительно, React кажется враждебным после элегантных декларативных Vue компонентов. Но считаю, что React большинство используют неправильно.
React - это НЕ фреймворк. Это БИБЛИОТЕКА для рендеринга разметки.
А все пихают туда логику и даже сетевое общение, как в Angular и Vue.
Что в корне неверно.
doomguy49
Подавляющее большинство проектов используют Vue2, а то и вообще какой-нибудь php шаблонизатор Blade