В прошлой статье мы рассказывали, как пришли к идее написать собственную панель управления хостингом и об общей структуре готовой панели.
Сегодня наш фронтенд разработчик Артыш расскажет, как писал фронтенд этой панели: какой выбрали фреймворк, какой антипаттерн считаем хорошим тоном и как защищаться от бекдоров, если пользуетесь готовыми библиотеками.
Выбор фреймворка: почему искали новый
Предыдущая панель была реализована на собственном фреймворке, написанном на jQuery. Мы сидели на VMManager, он требовал много доработок: по интерфейсу и функционалу, было тяжело сопровождать такой код. Добавление нового функционала в панель со стороны фронта занимало много времени. Понятно, что при желании и на jQuery можно реализовать хороший фреймворк (я до сих пор люблю jQuery) или даже подобие CMS, но это был не оптимальный вариант: начиная скудной документацией по самописному фреймворку и заканчивая не совсем корректной архитектурой самого приложения.
Старая панель была реализована в виде Single Page Application и на этом его хорошие качества заканчивались. После решения очередной головоломки по добавлению кнопки в список, пришло понимание, что нужна альтернатива. Выбор пал на Vue.
Почему SPA?
Single Page Application — идеальный выбор для панели управления. Панель управления в плане рендеринга довольно простая штука, эту работу можно легко доверить браузеру пользователя. К тому же панели не важна SEO-оптимизация, для этого у нас есть основной сайт. Ну и требуемое время на начальную загрузку всех необходимых скриптов пользователи панели воспринимают спокойно в силу специфики самих этих пользователей. Опять же, бекенд у нас получился классическим RestAPI сервисом — для предоставления в будущем открытого API нашим клиентам.
SPA приложение получилось таким легким, что хорошо работает с браузера телефонов и планшетов — мы просто сделали адаптивную верстку и создавать отдельное приложение не пришлось.
Почему Vue?
3 года назад Vue был относительно молодым фреймворком, но уже тогда о нем много говорили и писали, и когда вышел релиз версии 2.0, мы решили сделать ставку на него — и не прогадали. Сначала планировали просто постепенно заменять какие-то компоненты написанные на jQuery и Vue это позволял делать легко. Но потом, после того, как были переписаны довольно объемные компоненты, все таки решили, что лучше переписать вообще все приложение на Vue.
Это бы рискованный шаг и мы решили его сделать по 4 причинам:
- Vue — простой декларативный фреймворк, его понимают даже верстальщики. Если что, под него легко найти разработчика или просто научить товарища. А значит у нас не будет проблем с поиском нового разработчика и его вхождением в проект, если меня переедет трамвай (хвала богам, в моем городе их нет).
- Vue объективно хорош для написания SPA приложений.
- У меня перед глазами был опыт развития React и я предположил, что популярность Vue будет расти так же. Сейчас фреймворк входит в TOP-3 популярных JS-фреймворков (это легко проверить поисковым запросом), уступая только React и Angular. У него хорошая поддержка, развитая экосистема и большое комьюнити.
- Скорость разработки. Лично я сразу стал воспринимать Vue как этакий конструктор и разработка на нем идет довольно быстро: если мне нужен, например, компонент выбора даты, скорее всего на Vue он уже существует, свободен в использовании и опробован сообществом. Я просто устанавливаю компонент в проект, пишу тег и все работает. По сути, наша панель состоит на 70-80% из готовых библиотек. Я имею в виду именно использование компонента, а не размер кодовой базы, который можно проверить командой типа: npx intrinsic/loc
При реализации проекта всегда учитываешь его перспективы, особенно перспективы развития. И то, что в экосистеме Vue уже имеются такие инструменты как Weex, Quasar Framework или Nuxt по мне существенно расширяют горизонты развития нашей панели.
На Хабре есть
замечательная статья о Vue от его создателя, а я расскажу о некоторых особенностях нашего приложения.
Синхронизация Vuex с сервисом RestAPI
Часть данных глобального хранилища Vuex в нашем приложении синхронизируется с RestAPI путем обыкновенных запросов по соответствующим адресам. Зачем мы так сделали? Ну хотя бы для того, чтобы основные настройки пользователя не были привязаны к конкретному браузеру конкретного устройства. Можно зайти в нашу панель с компьютера жены или из игрового клуба и при этом получить в то же знакомое окружение, что и было у вас на своей родной машине.
Кроме того, когда синхронизация была только с localStorage, некоторые браузеры при обновлениях теряли содержимое localStorage — оно полностью удалялось. Да и в последнее время прослеживается какая то тенденция к ужесточению политики хранения данных пользователей в куках, например функция в WebKit Intelligent Tracking Prevention — не ровен час они доберутся и до localStorage.
Шина событий
Да, мы используем глобальную шину событий. Как и в любом другом крупном приложении с множеством компонентов, рано или поздно возникает необходимость наладить взаимодействие между не связанными между собой компонентами. Даже через глобальное хранилище. Понятно, что если есть связь родитель-потомок, их взаимодействие стандартно организуется через свойства props в одну сторону и методом $emit в другую, ну или через хранилище, как и
описано в рекомендациях Vue.
Но
в документации описана и возможность использования глобальной шины событий. У нас в проекте куча форм с разными наборами полей и в некоторых случаях (их немного, но все из них принципиальные) нужно как-то по особенному реагировать на изменение значения поля. Хранить в глобальном хранилище значения всех полей каждой формы не имеет смысла:
- Во-первых, из-за редкой необходимости
- Во-вторых, все наши формы генерируются динамически и набор полей у любой формы может поменяться кардинально.
Поэтому я решил использовать механизм шины событий. При этом ничто не мешает использовать свой Event emitter — главное использовать этот механизм аккуратно, только для исключительных ситуаций и тщательно все подчищать за собой.
Взаимодействие RestAPI с панелью
Для большей отзывчивости интерфейса в старом jQuery-фреймворке обратная связь от RestAPI к клиентскому приложению эмулировалась через хитрую систему таймеров: она производила опросы RestAPI с определенным интервалом и перерисовывала узлы DOM, которые затронули изменения.
Это не было идеальным решением: во всех современных браузерах таймеры практически полностью замирают, когда вкладка становится неактивной и получает низкий приоритет. Как результат запрос к RestAPI сервису может запоздать и получить уже неактуальные данные.
Для решения этой проблемы в новой панели я решил использовать связку из модуля Nchan для веб сервера Nginx и новых возможностей HTML5-интерфейсов — EventSource и WebWorker.
Модуль Nchan поддерживает отправку сообщений через Websocket, EventSource и Long-Polling. Я провел несколько тестов и решил использовать EventSource: сообщения могут быть только текстовыми и поток сообщений осуществляется только в одну сторону (от сервера). Это полностью решало поставленную задачу.
Сейчас работу интерфейса EventSource осуществляет в отдельном фоновом потоке WebWorker, независимо от активности вкладки. В этом же потоке организована примитивная очередь сообщений, чтобы ничего не потерялось. Очередь отправляется в основной поток приложения, который свою очередь производит необходимые перерисовки интерфейса, когда ему удобно и позволяет браузер.
Бэкдоры: как и зачем я проверяю безопасность компонентов
Перед подключением библиотеки ее обязательно проверить на безопасность: был случай, когда компонент специально внедрили бэкдор, который позволял заходить на сайт и скачивать данные.
Но чаще дыры в безопасности появляются скорее по неаккуратности разработчиков. В маркетах приложений есть команда, которая проверяет компоненты на безопасность, но она не слишком зубастая и библиотеки лучше проверять вручную.
Я всегда проверяю пакеты на наличие хуков preinstall, install и postinstall в поле “scripts” файла “package.json”. Кроме того, использую статические анализаторы пакетов, такие как retire, snyk и команду “audit” пакетного менеджера npm.
Предупреждения безопасности бывают разных уровней, чаще всего при проверке попадаются некритичные. Иногда, чтобы пролечить библиотеку достаточно обновиться — разработчики библиотек сами следят за безопасностью.
Если библиотека однажды себя скомпрометировала, лучше ее заменить — это признак неблагонадежности, поэтому при предупреждениях я выбираю искать другую библиотеку.
После того, как пакет прошел анализ, я обязательно фиксирую его версию. Если нужна другая версия — пакет проходит анализ заново. Да, это занимает время, но оно того стоит.
Пока бэкдоры ни разу не попадали к нам на продакшн.
Много-много комментариев
Как я уже говорил, Vue был выбран за простоту и декларативность. В дополнение к этому я пишу много комментариев, практически к каждой строчке: чтобы в случае чего новый разработчик мог легко войти в проект и чтобы я сам легко возвращался к старым кускам кода.
За что я полюбил новый фронтенд и панель в целом
- Стало проще поддерживать код
Разработка заняла полгода. Теперь я скорее занимаюсь поддержкой панели, свой код не жмет и не натирает.
Клиенты могут быстро получать то, что запрашивали
Стало быстро и удобно добавлять новые функции, которые появились в бекенде: например, оплату для юридических лиц я добавил за 2 дня, снепшоты — за 1 день.