«Едадил» — приложение-агрегатор скидок и акций. Оно появилось в ноябре 2012 года как независимый проект, но после было куплено Яндексом. С самого начала проекта разработка велась с использованием Vue. На митапе, организованном сообществом msk vue.js, Андрей Кобец, руководитель отдела разработки фронтенда «Едадила», рассказал, как его команда использует фреймворк, как они дорабатывают его проблемные места, и каково им живётся в Яндексе, где повсеместно используется React.
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.
Погружение в native
01:56 В 2016 году Едадил разрабатывает своё первое экспериментальное webview-приложение, встроенное в нативное, — раздел «Купоны». Требовалось обеспечить возможность работать в офлайне и обновлять код приложения без обновления через маркеты приложений.
03:20 Итоги эксперимента: запуск сервисов «Кэшбэк» (2017), «Карты лояльности» (2018), «Я в магазине» (2019).
Подводные камни в 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 — очень дорого.
Погружение в native 2.0
06:45 Веб-сервер в приложении Ktor/Kitura: общение через сокеты и нормальный сервинг статики.
07:31 Написали сервис конфигурации офлайна для нативных клиентов. После этого мобильное приложение может узнать, в какой версии операционной системы запущено, что и как кэшировать и какой бандл отдавать.
Организация кода компонентов в «Едадил»
07:54 Полное разделение всех сущностей: логика, стили, шаблоны. Не используют одностраничные компоненты, потому что они медленно собираются, и очень много кода в одном файле. Вместо этого есть папка с компонентом, где лежат стили/шаблоны/логика и всё это быстро собирается с помощью Webpack.
08:32 ОО-подход: компоненты наследуются от компонентов. Свой шаблонный движок, который поддерживает гибкое наследование. Самый главный Vue-компонент — это класс, который обрамлен специальным декоратором. Декораторы описывают метаинформацию.
08:55 Есть несколько версий кэшбека — Vue и web. Организованная слоёная система проектов позволила унаследоваться от базового проекта кэшбека, а всю остальную магию делает Webpack. Импорт в TS завязан не на файловой системе, а как будто используется библиотека из node_modules. Дальше Webpack сам подставит нужный путь.
Оптимизация приложения 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’а в Яндексе.
Ответы на вопросы
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?