Vue 3 станет быстрее
Одним из самых ярких событий в мире Фронтенда в этому году стала публикация репозитория Vue next — части функционала третьей версии VueJS. В этой в этой статье представлен обзор новых killer features VueJS. На момент публикации статьи репозиторий находился в статусе Pre-Alpha. Планы на релиз можно посмотреть в Roadmap
Предыстория
В феврале 2018 Evan You, создатель Vue.js, поделился планами на 3 версию популярного фреймворка:
- Разбить функциональность на пакеты для изоляции области видимости
- В кодовой базе появится TypeScript
- Vue 3 будет иметь обратную совместимость со 2й версией (т.е. не сломает старый код)
- Наблюдатели в 3.0 версии основаны на Proxy, что увеличит скорость рендера и снимет ряд ограничений накладываемых
Object.defineProperty
- Появится возможность дебажить с помощью новых хуков
renderTracked
иrenderTriggered
- Благодаря введению tree shaking (исключение из билда неиспользуемых директив) размер фреймворка составит меньше 10kb в сжатом виде
- Оптимизация слотов
- В 3й версии ожидается 100% улучшиения перфоманса
Features such as built-in components and directive runtime helpers (v-model) are now imported on-demand and tree-shakable
Evan You
Компилятор будет отслеживать наличие директив и включать их в билд на стадии компиляции.
В процессе работы над Vue 3 Эван отказался от переписывания компонентов на классы и вместо этого внедрил функциональное API.
Поскольку новая версия будет исользовать Proxy, которые не поддерживаются в IE, Эван планирует сделать отдельный билд для IE11. Всего обещают 4 фазы:
- Alpha Phase — стадия доработки компилятора и ssr-рендера
- Beta Phase — стадия доработки основных библиотек (Vue Router, Vuex, Vue CLI, Vue DevTools)
- RC Phase — стадия пререлиза, включающая в себя Vue 2.0
- IE11 build
- Final Release
Финальный релиз Эван запланировал на 2019 год, но проект все еще в стадии пре-альфа.
Vue 3 будет быстрее
Благодаря ряду нововведений Vue 3 станет в 2 раза быстрее предыдущей версии.
Proxy-based Observation and Reactivity
Одним из крупных нововведений стало изменение механизма наблюдения за объектами с геттеров и сеттеров Object.defineProperty на Proxy. Теперь Vue может отслеживать удаление и добавление свойств реактивных объектов без использования Vue.set и Vue.delete. Нововведение увеличило скорость рендеринга и скриптинга и уменьшило потребление памяти в 2 раза! Сравнить перфоманс Vue 2 и Vue 3 можно, скачав репозиторий Ильи Климова
Сравнение производительности Vue 2 (слева) и Vue 3 (стадия pre-alpha, справа)
Благодаря проксям не потеряется реактивность при изменении не отслеживаемых во Vue 2 манипуляций с объектами. Теперь Vue не будет рекурсивно проходить по свойствам объекта, чтобы вычислить изменения.
Что выполнено из обещаний:
- Компоненты-потомки и родители перерисовываются независимо
- Уменьшился размера Vue 3 с 20kb до 10kb в сжатом виде
- Добавлен TypeScript
Другие оптимизации:
- Vue 3 будет запоминать статичный контент и патчить только динамические данные
- Статичные пропсы поднимутся наверх области видимости
- Для простоты разработки код Vue 3 разбит на модульные пакеты
- Пакет runtime-core сделан кроссплатформенным
- Вместо классов Эван добавил setup функцию и хуки, благодаря которым код становится чистым, организованным и переиспользуемым*
- Time Slicing*. Выполнение JS кода нарезается на куски, не блокируя взаимодействие пользователя с приложением
Звездочками отмечено экспериментальное API.
Вдохновившись HOC Реакта Эван реализовал setup функции с переиспользуемой логикой и кастомными хуками. В отличие от миксинов хуки жизненного цикла не перезаписывают друг друга.
Усовершенствованный патч VDom
Хойстинг статического контента
Статичный контент выносится за пределы патчинге VDom при компиляции шаблона. На это команду Vue вдохновил Svelte:
Hello, {{name}}
Здесь передается объект changed и контекст. Если changed содержит реактивную переменную, то она обновляется в контексте.
p(changed, ctx) {
if(changed.name) {
set_data(t1, ctx.name);
}
}
В предыдущей реализации компилятор Vue проходился по всем нодам, включая статичные:
function render(){
const children = [];
for(let i = 0; i < 5; i++) {
children.push(h('p', {
class: 'text'
}, i === 2 ? this.message : 'Lorem upsum'))
}
return h('div', { id: 'content' }, children)
}
Новая стратегия компиляции шаблонов
В новой версии шаблон разбивается на блоки:
- Шаблон делится на блоки
- Структура нод внутри каждого блока полностью статична
- Для отслеживания динамических значений в блоке требуется только 1 плоский массив, куда они помещаются
С новой стратегией производительность напрямую зависит от количества динамическего контента вместо размера шаблона.
Vue 3 будет лучше адаптирован под большие проекты
Большие проекты сталкиваются со следующими проблемами при использовании Vue:
- Не идеальная поддержка TypeScript
- Массивные, сложно поддерживаемые компоненты
- Отсутсвие простого паттерна для переиспользования кода
Изначально для поддержки TS планировалось добавить классы. Но команда Vue столкнулась с проблемами:
Команде Эвана запросила помощь у экспертов из TC39 и выяснила, что подобная реализация могла бы конфликтовать с плагинами, которые примешивают различные пропсы и атрибуты к контексту Vue.
Потенциально эти проблемы могли решить декораторы, но они находятся в разработке.
Composition API
Команда Vue вдохновилась хуками Реакта и решила создать подобное API. Оно опционально и находится в стадии разработки и обсуждения, поэтому некоторые названия могут меняться.
Главная концепция этого изменения — организовать код компонента логичнее, разбив его на смысловые блоки. Подробнее про composition API можно прочитать в документации
Пример использования Vue 3. Компонент разбивается на логические функции, внутри которых можно исопльзовать реактивность и хуки жизненного цикла.
Импортируем новые хуки из composition API:
import { reactive, computed, onMounted } from '@vue/composition-api';
export default {
setup() {
const { state } = countAnimal("rabbit")
const { getType, anotherState } = anotherCount()
return {
state,
getType,
anotherState
}
}
}
В функции countAnimal есть реактивные свойства count, animal и метод increment. При нечетном счетчике имя животного меняется. Счетчик запускается при монтировании компонента.
function countAnimal(name) {
const state = reactive({
count: 0,
animal: computed(() => state.count % 2 ? name : 'bear')
})
function increment() {
setTimeout(() => {
state.count++;
increment()
}, 1000)
}
onMounted(() => {
increment()
})
return {
state
}
}
Создаем другую функцию anotherCount, которая тоже содержит метод increment и state с счетчиком и названием животного. В метод getType передается название животного из шаблона.
function anotherCount() {
const anotherState = reactive({
count: 0,
animal: 'fox'
})
function getType(name) {
return name == 'bear' ? 'slow' : 'fast'
}
function increment() {
setTimeout(() => {
anotherState.count+=10;
increment()
}, 1000)
}
onMounted(() => {
increment()
})
return {
getType,
anotherState
}
}
В шаблоне выводятся 2 счетчика и 2 имени животных. Тип бега меняется в зависимости от имени животного.
Count {{state.animal}}: {{ state.count }}
{{state.animal}} runs {{getType(state.animal)}}
Another:
Count {{anotherState.animal}}: {{ anotherState.count }}
Новые хуки используются внутри setup, не ломая старый API. Обратите внимание, что onMounted ссылается на единый хук жизненного цикла компонента.
У такого апи есть ряд преимуществ:
- Хуки жизненного цикла не перезатирают друг друга
- Понятен источник свойств
- Не создаются дополнительные экземпляры компонента
Заключение
Выше перечислены самые важные изменения во Vue 3. Большинство улучшений будут скрыты под капотом сгенерированного компилятором кода.
Основные улучшения:
- Сгенерированный код оптимальнее для JS компилятора
- Сгенерированный bundle легче
- Компоненты родителей/потомков перерисовываются благодаря улучшенному алгоритму патчинга
Vue успел зарекомендовать себя, как один из самых быстрых и оптимальных фреймворков. Новая версия станет еще быстрее и легче. Vue 3 отлично подходит для современного мобильно- и перфоманс-ориентированного веба. Комментарий о будущих изменениях можно оставить в официальном RFC (request for comments)