EVAN YOU: Создание Vue 3

Spread the love

Оригинальная статья: EVAN YOUThe process: Making Vue 3

Причины создания следующей основной версии Vue.js.

В течение прошлого года команда Vue работала над следующей основной версией Vue.js, которую мы надеемся выпустить в первой половине 2020 года. (Эта работа продолжается на момент написания этой статьи.) Идея новой версии Vue сформировалась в конце 2018 года, когда кодовой базе Vue 2 было около двух с половиной лет. Это может показаться не таким уж долгим периодом в жизни программного обеспечения, но среда фронтенда сильно изменилась за этот период.

Два ключевых соображения привели нас к новой версии Vue: во-первых, распространение общей доступности новых функций языка JavaScript в основных браузерах, во-вторых, проблемы дизайна и архитектуры в текущей кодовой базе, которые были выявлены с течением времени.

Зачем переписывать?

Новые особенности языка

Благодаря стандарту ES2015, формально известному как ECMAScript, сокращенно ES, JavaScript получил значительные улучшения, и основные браузеры наконец-то начали оказывать достойную поддержку этим новым дополнениям. Некоторые, в частности, предоставили нам возможность значительно улучшить возможности Vue.

Наиболее примечательным среди них является Proxy, который позволяет платформе перехватывать операции над объектами. Основная особенность Vue — это возможность прослушивать изменения, и оперативно обновлять DOM. Vue 2 реализует эту реактивность, заменяя свойства объектов состояния геттерами и сеттерами. Переход на Proxy позволит нам устранить существующие ограничения Vue, такие как невозможность обнаружения новых добавлений свойств, и обеспечить более высокую производительность.

Однако Proxy — это функция встроенная в язык, и она не может быть полностью имитирована в старых браузерах. Чтобы использовать ее, мы знали, что нам нужно будет ограничить диапазон поддержки браузеров в фреймворке — это серьезное изменение, которое может быть добавлено только в новой основной версии.

Решение архитектурных вопросов

Для исправления этих проблем в текущей кодовой базе потребуются огромные, рискованные изменения, которые почти эквивалентны переписыванию.

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

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

Начальная фаза прототипирования

Мы начали прототипирование Vue 3 в конце 2018 года с предварительной целью проверки решений этих проблем. На этом этапе мы в основном сосредоточились на создании прочной основы для дальнейшего развития.

Использование Typescript

Vue 2 был первоначально написан на простом ES. Вскоре после этапа создания прототипа мы поняли, что система типов будет очень полезна для проекта. Проверка типов значительно снижает вероятность появления непреднамеренных ошибок при рефакторинге и помогает программистам более уверенно вносить нетривиальные изменения. Мы попытались использовать проверку типов от Facebook Flow type checker , потому что она может быть постепенно добавлена к существующему проекту на простом ES. И она в некоторой степени помогла нам, но мы не получили от нее столько пользы, сколько надеялись; в частности, постоянные изменения делали болезненной модернизацию проекта. Поддержка интегрированных сред разработки также не была идеальной по сравнению с глубокой интеграцией TypeScript с кодом Visual Studio.

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

Разделение внутренних пакетов

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

Использование процесса RFC

К концу 2018 года у нас был рабочий прототип с новой системой реактивности и виртуальным рендером DOM. Мы проверили внутренние архитектурные улучшения, которые мы хотели сделать, но имели только черновики общих изменений API. Пришло время превратить их в конкретные конструкции.

Мы знали, что должны были сделать это быстро и тщательно. Широкое использование Vue означает, что серьезные изменения могут привести к огромным затратам на миграцию для пользователей и потенциальной фрагментации экосистемы. Чтобы пользователи могли предоставлять отзывы о критических изменениях, мы начали использовать процесс RFC (Запрос на комментарии) в начале 2019 года. Каждый RFC следует шаблону, разделы которого сосредоточены на мотивации, деталях дизайна, компромиссах и принятии и стратегии. Поскольку процесс проводится в репозитории GitHub с предложениями (proposals), представленными в виде pull запросов, и постепенно начались обсуждения в комментариях.

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

Быстрее и меньше

Производительность имеет важное значение для любого веб фреймворка.

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

Преодоление ограничений виртуального DOM

У Vue довольно уникальная стратегия рендеринга: она предоставляет синтаксис HTML-подобного шаблона, но компилирует шаблоны в функции рендеринга, которая возвращает виртуальные DOM-деревья. Фреймворк вычисляет, какие части фактического DOM обновились, путем рекурсивного обхода двух виртуальных DOM деревьев и сравнения каждого свойства на каждом узле. Этот довольно грубый алгоритм, как правило, довольно быстрый, благодаря продвинутым оптимизациям, выполняемым современными механизмами JavaScript, но обновления по-прежнему требуют много ненужной работы процессора. Неэффективность особенно очевидна, когда вы используете шаблон со значительной степенью статическим содержимым и только несколькими динамическими привязками — все виртуальное дерево DOM все еще необходимо рекурсивно обойти, чтобы выяснить, что изменилось.

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

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

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

Во-первых, на уровне дерева мы заметили, что структуры узлов остаются полностью статичными в отсутствие шаблонных директив, которые динамически изменяют структуру узлов (например, v-if и v-for). Если мы разделим шаблон на вложенные «блоки», разделенные этими структурными директивами, структуры узлов внутри каждого блока снова станут полностью статичными. Когда мы обновляем узлы в блоке, нам больше не нужно рекурсивно обходить дерево — динамические привязки внутри блока можно отслеживать в плоском массиве. Эта оптимизация позволяет обойти большую часть накладных расходов виртуального DOM, уменьшив на порядок объем обхода дерева, который нам нужно выполнить.

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

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

В совокупности эти методы значительно улучшили наши тесты обновления рендеринга, причем компиляция с Vue 3 иногда занимала менее одной десятой CPU time Vue 2. (CPU time это время, затрачиваемое на выполнение JavaScript-вычислений, за исключением операций DOM браузера.)

Минимизация размера сборки

Размер фреймворка также влияет на его производительность. Это уникальная проблема для веб-приложений, поскольку ресурсы нужно загружать на лету, и приложение не будет интерактивным, пока браузер не проанализирует необходимый JavaScript-код. Это особенно верно для одностраничных приложений. Несмотря на то, что сборка Vue всегда была относительно небольшим (размер среды выполнения Vue 2 составляет около 23 КБ), мы заметили две проблемы:

Во-первых, не все используют все возможности фреймворка. Например, приложение, которое никогда не использует функции перехода (transition), по-прежнему загружают ее поддержку.

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

В идеале пользователь должен иметь возможность отбрасывать код неиспользуемых им функций во время сборки, такой функционал известен как «встряхивание дерева» (“tree-shaking”), и включать только то, что он использует. Это также позволило бы нам добавлять функции, которые небольшая группа наших пользователей сочла бы полезными, без добавления затрат на полезную нагрузку для остальных.

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

Некоторые части инфраструктуры никогда не могут быть удалены, потому что они необходимы для любого типа приложения. Мы называем размер этих обязательных частей базовым размером. Базовый размер Vue 3 составляет около 10 КБ в сжатом виде — это менее половины размера Vue 2, несмотря на добавление многочисленных новых функций.

Решение проблемы масштабирования

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

На ранних этапах разработки Vue 3 мы пытались улучшить интеграцию TypeScript, предлагая встроенную поддержку для разработки компонентов с использованием классов. Проблема заключалась в том, что многие языковые функции, которые нам были необходимы, чтобы сделать классы пригодными для использования, такие как поля классов и декораторы, все еще были предложениями и подлежали изменению, прежде чем официально стать частью JavaScript. Сложность и неопределенность заставили нас усомниться в том, действительно ли добавление Class API было оправдано, поскольку оно не предлагало ничего, кроме немного лучшей интеграции с TypeScript.

Мы решили исследовать другие способы решения проблемы масштабирования. Вдохновленные React Hooks, мы подумали об использовании низкоуровневых API-интерфейсов реактивности и жизненного цикла компонентов, чтобы обеспечить более свободный способ создания логики компонентов, называемый Composition API. Вместо определения компонента путем указания длинного списка параметров, Composition API позволяет пользователю свободно выражать, создавать и повторно использовать логику компонентов с состоянием точно так же, как при написании функции, обеспечивая при этом превосходную поддержку TypeScript.

Мы были очень рады этой идее. Хотя Composition API был разработан для решения определенной категории проблем, технически возможно использовать его только при разработке новых компонентов. В первом проекте предложения мы немного опередили себя и намекнули, что мы можем заменить существующий API параметров (Options) на API композиции в будущем релизе. Это привело к массированному отклику со стороны членов сообщества, который преподал нам ценный урок о четкой передаче долгосрочных планов и намерений, а также понимании потребностей пользователей. Выслушав отзывы от нашего сообщества, мы полностью переработали предложение, пояснив, что Composition API будет дополнять текущее Options API. Отзывы на пересмотренного предложение было гораздо более позитивным и получило много конструктивных предложений.

Поиск баланса

Сколько разработчиков столько вариантов использования.

Среди пользователей Vue более миллиона разработчиков — новички, обладающие только базовыми знаниями HTML / CSS, профессионалы, переходящие с jQuery, ветераны, переходящие с другого фреймворка, бэкенд-инженеры, ищущие решение для своего фронтенда, и архитекторы программного обеспечения, работающие с программным обеспечением в масштабе. Разнообразие профилей разработчиков соответствует разнообразию вариантов использования: некоторые разработчики могут захотеть добавить интерактивность в унаследованные приложения, в то время как другие могут работать над одноразовыми проектами с быстрым оборотом, но с ограниченными проблемами; архитекторам, возможно, придется иметь дело с крупномасштабными многолетними проектами и постоянно меняющейся командой разработчиков в течение всего срока поддержки проекта.

Дизайн Vue постоянно формировался и учитывал эти потребности, поскольку мы стремимся найти баланс между различными компромиссами. Слоган Vue, «прогрессивная структура», заключает в себе многоуровневый API-дизайн, который вытекает из этого процесса. Новички могут наслаждаться плавным обучением благодаря сценарию CDN, шаблонам на основе HTML и интуитивно понятному API параметров, в то время как эксперты могут обрабатывать амбициозные сценарии использования с полнофункциональным интерфейсом командной строки, функциями рендеринга и Composition API.

Нам еще предстоит проделать большую работу, чтобы реализовать наше видение — самое главное, обновить вспомогательные библиотеки, документацию и инструменты для обеспечения плавного перехода. Мы будем усердно работать в ближайшие месяцы, и мы с нетерпением ждем, то что сообщество создаст в ближайшее время с помощью Vue 3.

Была ли вам полезна эта статья?
[3 / 5]

Spread the love

Добавить комментарий