19 октября 2024 года завершился Vue Fes Japan 2024 — традиционное событие, которое собрало множество энтузиастов и экспертов в области веб-разработки, где рассказывали о будущем экосистемы вью. На этой конференции разработчик Кевин Денг подробно представил новый этап в эволюции фреймворка Vue — Vapor Vue. Ожидается, что Vapor Vue значительно повысит скорость работы фреймворка, делая его еще более эффективным и мощным инструментом для создания современных веб-приложений. Такой шаг в развитии не только обещает ускорение производительности, но и открывает новые горизонты для гибкости и адаптивности Vue в работе над сложными проектами. В данной статье мы расскажем о самом интересном в этой презентации.
Что такое Vapor
Vue Vapor представляет собой новый механизм рендеринга, который имеет следующие ключевые особенности:
Прямая работа с нативными API DOM.
Технология больше не полагается на виртуальный DOM, вместо этого напрямую вызывает нативные API DOM. Это позволяет уменьшить количество издержек, повышает производительность и снижает потребление памяти, поскольку сокращается количество промежуточных шагов и уменьшается нагрузка на систему.Уменьшение размера итогового бандла.
Этот подход уменьшает размер итогового бандла. При прямом использовании нативного API DOM исключается необходимость в коде для обработки виртуальных узлов DOM, что приводит к более компактному и быстро загружаемому приложению, облегчающему использование, особенно для пользователей с медленным интернетом.Fine-grained rendering.
Теперь, основываясь на системе реактивности vue, можно точно отслеживать изменения данных и обновлять только то, что действительно необходимо. Это позволяет избежать повторного рендеринга всего компонента и поиска измененных узлов виртуального DOM.
Уменьшение бандла
Ранее я упоминал о важности уменьшения размера бандла. В результате использования прямого взаимодействия с нативными API DOM размер бандла сократился на впечатляющие 53,3% по сравнению со стандартным режимом виртуального DOM. Эти данные не учитывают размер самой системы реактивности, но даже без этого уменьшение уже является значительным и положительно сказывается на производительности приложения.
Ускорение производительности
Если же говорить про скорость работы, то за абсолютное значение можно взять ванильный js, который будет представлять собой 100%. Дальше идёт текущий рекордсмен — Solid, следом идёт многим полюбившийся Svelte. Теперь же Vue Vapor работает наравне со Svelte, что весьма впечатляет. Он заметно быстрее, чем режим Vue vDOM и React с Jotai. Несмотря на то, что Vapor уже превосходит Vue vDOM и React, его еще можно улучшить, и команда стремимся сделать его еще лучше.
Vapor не является отдельным режимом
Vapor — по своей сути, лишь подмножество vDOM, что приводит к некоторым ограничениям.
Текущий режим виртуального DOM (vDOM) поддерживает множество функций, таких как Options API, Composition API, пользовательские директивы, миксины и другие.
Vapor поддерживает исключительно Composition API и не включает Options API. Однако в будущем Options API может быть доступен через стороннюю библиотеку на основе отзывов пользователей.
Для работы с Vapor необходимо использовать инструменты сборки, такие как Babel, так как компилятор Vapor сложен и не позволяет запускать код напрямую в браузере через CDN, в отличие от vDOM. Запуск в браузере замедлил бы процесс и увеличил размер бандла, снижая эффективность по сравнению с vDOM.
Кроме того, Vapor поддерживает только <script setup>, что исключает экспорт компонентов через обычный <script>. Это решение основано на признании <script setup> лучшей практикой для использования Composition API. Исключение Options API позволяет компилятору более эффективно оптимизировать код.
Как работает Vapor
Давайте разберём принципы Vue Vapor поэтапно.
Для начала упрощённо представим создание простого приложения-счётчика с использованием нативных DOM API. Сначала создаём `div`, заголовок и кнопку, после чего добавляем их в тело страницы. Затем объявляем переменную `count`, которая будет хранить текущее значение счётчика. После этого добавляем обработчик события для кнопки, который при каждом нажатии увеличивает значение `count` на единицу. Этот обработчик также вызывает функцию `render`, которая отвечает за обновление отображения счётчика на странице.
// Initialization
const container = document.createElement('div')
const label = document.createElement('h1')
const button = document.createElement('button')
button.textContent = 'Increase'
button.addEventListener('click',increase)
let count = 0
const render = () => {
label.textContent = `Count: ${count}`
}
function increase() {
count++
render() // Re-render
}
render() // Initial Reneder
document.body.append(container)
container.append(label, button)
Теперь, если использовать `@vue/reactivity`, мы можем сделать `count` реактивным. Достаточно обернуть `count` в `ref` и обновлять его через `count.value++` в функции увеличения. Ранее мы вызывали функцию рендеринга вручную, чтобы обновить страницу, но теперь можно использовать функцию `effect`, в которую внутрь поместим функцию рендеринга. Благодаря этому функция рендеринга автоматически срабатывает при изменении реактивного `count`.
import {effect, ref} from '@vue/reactivity'
// Initialization
const container = document.createElement('div')
const label = document.createElement('h1')
const button = document.createElement('button')
button.textContent = 'Increase'
button.addEventListener('click',increase)
let count = ref(0)
effect(()=>{
label.textContent = `Count: ${count.value}`
})
function increase() {
count.value++
}
document.body.append(container)
container.append(label, button)
Это простая демонстрация работы с реактивностью, но представьте, что есть и другие функции, например, уменьшение. При использовании этих функций нужно было бы следить за вызовами функции рендеринга каждый раз, что могло бы привести к путанице. Также простое перерисовывание всего замедлило бы производительность.
Используя реактивность, мы можем больше не беспокоиться о том, где нужны обновления, или вручную вызывать функцию рендеринга. Это и есть основная идея Vue Vapor: с помощью `@vue/reactivity` отслеживать изменения данных и автоматически обновлять только те DOM-узлы, которым это действительно необходимо.
Вспомним на минутку React, а точнее, его концепцию рендеринга. Её можно представить формулой UI = fn(state), где state и ui представляют собой снапшоты в определенный момент времени. Когда состояние изменяется, создается новый снапшот UI, который сравнивается с нативными DOM-узлами.
В отличие от этого, в Vue в режиме виртуального DOM (vDOM) UI также остается снапшотом, но состояние является реактивным. При изменениях состояния Vue повторно запускает функцию рендеринга для создания нового снапшота UI, что похоже на поведение React.
Однако в Vue Vapor ни состояние, ни UI больше не являются снапшотами. Функция рендеринга выполняется только один раз, создавая один объект UI. Когда состояние меняется, мы не создаём новый DOM-объект, а работаем с уже существующим: напрямую меняем его атрибуты. Это более действенный метод.
Внутри снапшот неизменяем, но в Vapor объект UI изменяем внутри, хотя кажется неизменным снаружи. Это ключевое отличие между Vapor и моделью виртуального DOM.
Vapor SFC Compilation
Теперь, когда мы поняли основной принцип Vapor, рассмотрим пример компиляции однофайлового компонента (SFC) с использованием Vapor. SFC относится к файлам .vue и практически идентичен SFC в режиме vDOM.
Основное отличие состоит в атрибуте `vapor`, добавленном к тегу script, указывающем, что это компонент Vapor.
<script setup vapor>
import { ref } from 'vue'
const count = ref(0)
function increase() {
count.value++
}
</script>
<template>
<h1>Count: {{ count }}</h1>
<button @click="increase">Increase</button>
</template>
Рассмотрим скомпилированный код SFC поэтапно:
import { ref } from 'vue'
import { delegate, delegateEvents, renderEffect, setText,
template } from 'vue/vapor'
const t0 = template('<h1>')
const t1 = template('<button>Increase')
delegateEvents('click')
export default {
setup() {
const count = ref(0)
function increase() {
count.value++
}
const n0 = t0()
const n1 = t1()
delegate(n1, 'click', () => increase)
renderEffect(() => setText(n0, 'Count: ',
count.value))
return [n0, n1]
},
}
Сначала шаблон компилируется в отдельные фрагменты, а обработка событий перенаправляется на корневой узел. Это уменьшает количество слушателей событий и улучшает производительность.
Затем мы определяем переменную `count` и функцию `increase` внутри `setup`.
Последние строки кода обрабатывают основную логику рендеринга. Мы связываем обработчик событий с узлом и используем `renderEffect` для отслеживания любых обновлений данных.
После этого при необходимости обновлений выполняется только код внутри `renderEffect`.
Структура сборки
Структура Vapor отличается гибкостью благодаря следующему процессу:
1. Общая логика вызовов для однофайлового компонента (SFC): сначала используется верхнеуровневый плагин Vite, который вызывает компилятор SFC.
2. Компилятор SFC извлекает тег шаблона и передаёт его компилятору Vapor, который затем компилирует его в JavaScript код.
Внутренний процесс включает:
Фазу разбора: содержимое разбирается в абстрактное синтаксическое дерево (AST).
Фазу трансформации: AST преобразуется в промежуточное представление (IR).
Фазу генерации: IR преобразуется в JavaScript код, который является конечным результатом компиляции.
IR (Intermediate Representation) — это промежуточное представление, используемое Vapor. Оно выступает связующим звеном между шаблоном и финальным JavaScript кодом.
IR определяет ключевые действия, такие как создание фрагментов шаблона, привязка событий, вставка или добавление DOM-узлов, настройка атрибутов или свойств и другие.
Это означает, что независимо от используемого синтаксиса шаблона, если он может генерировать целевой IR в соответствии со стандартами Vapor, мы можем скомпилировать его в код для выполнения Vapor.
IR может быть адаптирован для различных синтаксисов шаблонов, таких как шаблоны Vue, Svelte, JSX и другие, что упрощает поддержку различных синтаксисов шаблонов.
Нужно ли переходить на Vapor сразу же полностью?
Ответ — не обязательно. Компоненты Vapor будут совместимы с режимом vDOM и наоборот. Конечно, будет поддержка и чистого режима Vapor.
Это позволяет осуществлять переход более плавно, без необходимости мгновенного перевода всего проекта на Vapor. Можно сначала мигрировать критически важные для производительности компоненты на Vapor, оставив остальные в режиме vDOM
Подводя итоги
Vapor — это новое начало для Vue. Было переписано почти всё с нуля.
Vapor является подмножеством режима vDOM Vue, с акцентом на упрощение.
Vapor на текущий момент сильно оптимизирует производительность и размер сборки.
НЛО прилетело и оставило здесь промокод для читателей нашего блога:
-15% на заказ любого VDS (кроме тарифа Прогрев) — HABRFIRSTVDS.
Комментарии (29)
gmtd
07.11.2024 09:33Непонятно, это перевод или авторская статья?
Где ссылка на оригинал?YukinoKingu Автор
07.11.2024 09:33Перевод, а если быть точнее адаптации презентации - https://talks.sxzz.moe/2024-10-vue-fes-japan/1.
ссылка, как обычно, указана в шапке.gmtd
07.11.2024 09:33Тогда это не перевод, а ваша статья называется
В презентации - презентация только
AcckiyGerman
07.11.2024 09:33Каждые пару лет добавлять и убирать программных сущностей по вкусу, раз в три года
Было переписано почти всё с нуля.
ведь просто работающий фреймворк это же скучно. А так люди заняты обслуживанием, обновлением, переписыванием. Но пока Vue отстаёт в переливании пустого в порожнее от Ангуляра, который давеча 18 версию разменял.
gmtd
07.11.2024 09:33А так люди заняты обслуживанием, обновлением, переписыванием.
Ну, вообще-то, это его личный проект
Отдельный от Vue
karrakoliko
Но ведь несколько лет назад, когда завозили виртуальный DOM, рассказывали что именно эти проблемы он и призван решить?
DDroll
"Я соврал" - (с)Джон Матрикс, кф "Коммандо"
nin-jin
Вам постоянно врут. И даже сейчас наврали, утверждая, что ловля всех событий в одной точке эффективней, чем точечная ловля событий в разных точках. Фактически они на JS переизобретают то, что в браузерах уже реализовано на C++.
gmtd
Вряд ли когда-то про эти проблемы говорили
VDOM решает другую проблему - более понятный и структурированный код, лучший DX, особенно на больших проектах
Mingun
Да нет, я отчётливо помню, что говорили именно про эти проблемы. А понятность и структурированность кода — понятие ортогональное.
Alexufo
https://svelte.dev/blog/virtual-dom-is-pure-overhead
kpbsod
Главное же здесь — это продать. Тогда это было модно и продавалось. Сейчас в моде отказ от этого. В будущем, уверяю, что-то ещё придумают)
nin-jin
Я всё жду, когда же, наконец, придумают виртуализацию рендеринга.
gpin
Интересно, но не смог дочитать. На iPhone страница часто вылетает с ошибкой. На параграфе про ооп против фп становится совсем уж тяжко - 30 секунд и пока. При этом каждый раз заново скроллить вниз трудно - не ухватывается скроллбар.
nin-jin
А что за ошибка? И что за скроллбары на Айфоне?
Pubert
У этого фреймворка рендеринг настолько виртуальный, что он явно происходит где-то за пределами моего устройства, от чего я на их сайте вижу только белый экран. Но это такая фича, не волнуйтесь. Новейшая оптимизация, так сказать
nin-jin
Вы, главное, не говорите, какое у вас устройство.
Mingun
Забавно, что автор в той статье много говорит о решении проблемы с прыгающим скроллом, при этом в самой статье скролл прыгает только так. Бесит, если честно. Периодически при кручении колёсика прокручивается чуть ли не вся страница (я так полагаю, в момент подгрузки контента) вместо 3-4 строк, как обычно.
Ну и дизайн сайта очень странный:
Firefox 115.17.0esr (ибо Windows 7)
nin-jin
На экране у вас 4 панели, в каждой из которых своя прокрутка. Слева главное меню, справа личные закладки, по центру оглавление энциклопедии и текущая страница. Экран у вас большой, так что осталось места ещё и на воздух между ними.
Касательно прокрутки - хз, в актуальной панде всё работает как надо.
И, кстати, вот вариант без главного меню (привет от микрофронтендов): https://page.hyoo.ru/#!=gjboo1_xhyrnq