Опыт использования Vue.js в «Едадиле»: как применяют и почему не выбрали React

«Едадил» — приложение-агрегатор скидок и акций. Оно появилось в ноябре 2012 года как независимый проект, но после было куплено Яндексом. С самого начала проекта разработка велась с использованием Vue. На митапе, организованном сообществом msk vue.js, Андрей Кобец, руководитель отдела разработки фронтенда «Едадила», рассказал, как его команда использует фреймворк, как они дорабатывают его проблемные места, и каково им живётся в Яндексе, где повсеместно используется React.

1

Vue 0.x — 2.x

00:00 В «Едадиле» Vue начал использоваться ещё с версии 0.11. Уже тогда он был сильно быстрее и проще Angular.js с его dirty-check и лаконичнее React с его setState и shouldComponentUpdate (не говоря уже про JSX).

00:49 Вышедший летом 2015 Vue 1 ознаменовал курс на стабилизацию. Это было весело: two-way binding, всеобщая изменяемость и, конечно, белые экраны пока не скомпилится шаблон.

01:21 Весной 2016 произошла революция — вышел Vue 2. Больше никакого веселья. Нет two-way binding, неизменяемые входные параметры, VDOM и, конечно же, прекомпиляция шаблонов. Это первая версия Vue, которая стала использоваться на edadeal.ru.

2

Погружение в native

01:56 В 2016 году Едадил разрабатывает своё первое экспериментальное webview-приложение, встроенное в нативное, — раздел «Купоны». Требовалось обеспечить возможность работать в офлайне и обновлять код приложения без обновления через маркеты приложений.

03:20 Итоги эксперимента: запуск сервисов «Кэшбэк» (2017), «Карты лояльности» (2018), «Я в магазине» (2019).

3

Подводные камни в native

03:33 Разные версии webview — в новом смартфоне под Android 8 может быть с необновляемый webview c версией Chrome 42.

04:12 Проблема с офлайном. В iOS webview нет ServiceWorkers, а в Android может быть, а может не быть. Делали всё инлайном в HTML. Всю статику кодировали в Base64. Сервер по специальному протоколу предварительно кэшировал нативный клиент.

04:56 Проблема в общении с нативным клиентом. В iOS только postMessage, в Android есть перехват запросов, но он синхронный.

05:54 Сервер отдавал бинарные данные, а разработчики могли использовать только строковые сообщения с нативом. Поэтому array-буфер protobuf кодировался в Base64, и огромная строка кидалась в webview-клиент.

06:22 Проблема скорости: Vue тормозит в webview, общение с нативом стоит дорого, а любое изменение наблюдаемого свойства на устройствах с Android 4/5, iOS 9 — очень дорого.

4

Погружение в native 2.0

06:45 Веб-сервер в приложении Ktor/Kitura: общение через сокеты и нормальный сервинг статики.

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

5

Организация кода компонентов в «Едадил»

07:54 Полное разделение всех сущностей: логика, стили, шаблоны. Не используют одностраничные компоненты, потому что они медленно собираются, и очень много кода в одном файле. Вместо этого есть папка с компонентом, где лежат стили/шаблоны/логика и всё это быстро собирается с помощью Webpack.

08:32 ОО-подход: компоненты наследуются от компонентов. Свой шаблонный движок, который поддерживает гибкое наследование. Самый главный Vue-компонент — это класс, который обрамлен специальным декоратором. Декораторы описывают метаинформацию.

08:55 Есть несколько версий кэшбека — Vue и web. Организованная слоёная система проектов позволила унаследоваться от базового проекта кэшбека, а всю остальную магию делает Webpack. Импорт в TS завязан не на файловой системе, а как будто используется библиотека из node_modules. Дальше Webpack сам подставит нужный путь.

6

Оптимизация приложения Vue

09:48 В Vue есть функциональные компоненты, но в них есть только prop. Cтали использовать функциональные компоненты до появления шаблонов, так как любая скомпилированная рендер-функция внутри себя вызывает this.createElement. Этой функции можно дать свой контекст, и у неё появятся и методы, и геттеры, и наблюдаемые свойства.

10:55 Зачем думать какой компонент лучше: обычный или функциональный. Лучше пусть за нас думает компилятор. Для этого делаются пометки в декораторе и компилятор в рантайме генерирует два компонента: один обычный, другой функциональный, — а на этапе сборки проекта выберет, какой подойдет лучше, и положит его в бандл.

11:47 Для улучшения производительности функциональных компонентов обернули createElement для склейки рендер-функций.

12:30 Используется несколько root-инстансов Vue. Изменение состояния затрагивает только конкретный инстанс. Хорошо подходит для асинхронного рендеринга и ленивой загрузки.

13:19 Отложенный рендер. Используется вместе с root-инстансами. Компоненты, которые, не видны на экране, уходят в планировщик потоков. Планировщик создаёт отдельный root-инстанс. Когда поступает сигнал, что нужно что-то зарендерить, сам рендерит и вставляет в DOM. Всё это происходит автоматически.

14:00 Нужно было избавится от лишних изменений Vue state. Зачем каждый раз тревожить Vue при изменении стейта компонента, если есть модификаторы.

14:48 Базовый lifecycle может сказать, что элемент mounted или создан, но он не может сказать, что элемент загружен или грузится. Поэтому создали дополнительный lifecycle, который идёт параллельно с базовым и может дать эту информацию.

16:08 Роутинг. Vue-роутер классный, но при любом push’е Vue начинает проверять всю страницу. На этой проверке он может зависнуть на пару секунд. Поэтому создали свой роутер, совместимый с Vue-роутером с точки зрения API. При переходе между страницами с одинаковыми именами вместо изменения наблюдаемого свойства он распространяет события на компоненты, которые сами к нему подписываются, чтобы не вызывать каскад проверок.

17:01 Замена Vue run time и засилие React’а в Яндексе.

7

Ответы на вопросы

18:52 Будет ли код доступен в open source?

19:40 Почему Vue, а не React? Перейдёте ли на Vue 3? (Спойлер: да)

20:56 О работе без соединения.

22:20 Что показывал профайлер при тормозах?

24:22 Не увеличивается ли бандл из-за функциональных компонентов?

25:10 Когда всё будет выложено на GitHub?

25:25 Как дела с прокси в Safari и Chrome 45?

26:04 Есть ли своя телефонная ферма?

26:39 Как собираются ошибки из клиента?

27:40 Об организации отрисовки в веб-приложении.

29:40 Как устроиться к вам на работу?

30:33 Единоличная ли была разработка?

30:58 Насколько долго нужно погружаться в проект Vue-программистам?

31:47 Подходит ли всё описанное для других проектов?

33:13 Рассматривали ли SvelteJS как альтернативу Vue?