Манифест 12-факторных приложений внес весомый вклад в процесс разработки и эксплуатации веб-приложений, но это по-большей части коснулось бекендов, и обошло стороной фронтенды. Большенство пунктов манифеста или не применимы к фронтендам, или выполняются сами собой, но с номером 3 конфигурация есть вопросики.
В оригинальном манифесте сказано: Сохраняйте конфигурацию в среде выполнения. На практике это означает, что конфигурацию нельзя хранить внутри исходного кода или финального артефакта. Ее нужно передавать в приложение по время запуска. У этого правила есть практические применения, вот парочка:
Приложение в разных окружениях должно обращаться к разным бекендам. На продакшене к продакшн API, на стейдженге к стейджинг API, а при запуске интеграционных тестов к специальному мок-серверу.
Для е2е тестов нужно снижать время ожидания реакции пользователя. Например, на если на сайте после 10 минут бездействия что-то происходит, то для тестового сценария можно уменьшить этот интервал до минуты.
Если фронтенд-приложение содержит в себе SSR, то задача
становится чуточку легче. Конфигурацию передают как переменные
окружения в приложение на сервере, при рендере она попадает в ответ
клиенту как глобальные переменные, объявленные в
<script>
в самом начале страницы. На клиенте же
достаточно эти переменные подхватывать из глобальной области
видимости и использовать.
Недавно мы в Авиасейлс делали приложение для серверного рендеринга кусочков сайта и столкнулись с этой задачей. Результат мой тиммейт заопенсорсил isomorphic-env-webpack-plugin.
Прекрасный Next.js умеет так из коробки, ничего специально делать не нужно.
Другое дело, если приложение набор статических файлов. Переменные окружения нельзя передать на клиент, ведь мы не контролируем сервер, а все файлы уже заранее сгенерированы и лежат на диске. В таком случае, на этот фактор часто забивают.
Два самых популярных способа хранить конфигурацию фронтенд приложения с клиентским рендерингом:
Положить к исходным текстам завести в коде файлик
config.js
, и сложить в него параметры. Это работает,
но некоторые крайние случаи могут сильно испортить жизнь. Часто в
таких файлах появляется проверка если текущий хост такой-то, то
ходить на дев-бекенд, если хост другой ходить на прод-бекенд. Такой
подход плохо дружит с переменным количеством окружений когда на
каждый PR поднимается новый стенд.
Передавать при сборке артефакта. Обычно это делают через
DefinePlugin
для Webpack. Этот подход лучше первого
для каждого окружения можно собирать отдельный артефакт и добавлять
туда специфичные параметры. Возникает проблема на продашкн поедет
не тот же артефакт, что тестировался. Технически, нет
гарантии, что в разных окружениях версия будет работать одинаково.
Иногда это приводит к печальным последствиям.
Есть несколько способов получше.
Подавляющее большенство фронтенд-приложений подчиняются двум критериям:
Файлы ресурсов раздаются через nginx, либо перед приложением стоит nginx как реверс-прокси. Тут nginx можно заменить на любой аналог.
Единственная конфигурация, необходимая приложению адреса разных API.
Для таких приложений проблему конфигурации можно решить так в
клиентском коде все запросы отправить на текущий домен, а на
стороне nginx роутить эти запросы в конкретные бекенды. Будем
отправлять запросы на /user-api/path
вместо
https://user.my-service.io/path
, на
/auth-api/path
вместо
https://auth.other-service.io/path
и так далее.
Дальше инструкция специфична для nginx в Docker-контейнере
Начиная с версии 1.19 официальный Docker-образ nginx умеет
использовать переменные окружения в конфигурационных файлах. Для
этого нужно создать файл конфигурации с суффиксом
.template
и поместить его в директорию
/etc/nginx/templates
. При старте сервер подхватит
переменные окружения, пройдёт по шаблонам и создаст финальные файлы
конфигурации.
Типичная конфигурация nginx для SPA будет выглядеть так:
server { listen 8080; root /srv/www; index index.html; server_name _; location /user-api { proxy_pass ${USER_API_URL}; } location /auth-api { proxy_pass ${AUTH_API_URL}; } location / { try_files $uri /index.html; }}
Dockerfile в этом случае будет примерно таким:
FROM node:14.15.0-alpine as buildWORKDIR /app# сборка фронтенд ассетов# ...FROM nginx:1.19-alpineCOPY ./default.conf.template /etc/nginx/templates/default.conf.templateCOPY --from=build /app/public /srv/wwwEXPOSE 8080
Теперь достаточно запустить контейнер и передать переменные окружения, на основе которых nginx создаст конфигурационные файлы.
Так, косвенным образом, фронтенд приложение получит параметры в момент запуска.
Живой пример можно посмотреть в этом проекте.
Такую схему можно реализовать и с другими серверами. Caddy поддерживает переменные окружения в конфигурационных файлах из коробки, а Traefik умеет в динамические конфигурации.
Если в приложении конфигурация не исчерпывается путями до API, вариант с прокси-сервером для хранения параметров не подходит.
Проделав этот путь можно пойти дальше и генерировать конфигурационные файлы не для прокси-сервера, а напрямую для фронтенда. Это позволит передавать любые параметры, не зависеть от способа раздачи статических файлов и места их хранения.
При старте приложения можно подхватывать переменные окружения и записывать их в JS-файл:
window.__ENV__ = { USER_API_URL: 'https://user.my-service.io/', AUTH_API_URL: 'https://auth.other-service.io/',};
А потом раздавать этот файл тем же способом, что и остальные
статические файлы. На клиенте забирать параметры из этой глобальной
переменной и использовать в приложении. Дополнительно нужно будет
добавить <script>
в HTML-страницу с
приложением.
Дальше инструкция специфична для nginx в Docker-контейнере
Важно отметить, что отправлять все переменные окружения на
клиент может быть опасно, часто в них хранится приватная информация
ключи доступа до API, пароли и токены. Поэтому, лучше явно
перечислить имена переменных, которые будут отправлены в браузер в
файле env.dict
:
BACK_URLGOOGLE_CLIENT_ID
Теперь простым Bash-скриптом generate_env.sh
будем
доставать значения из окружения и складывать в JS-файл:
#!/bin/bashfilename='/etc/nginx/env.dict'# Начало JS-файлаconfig_str="window._env_ = { "# Конкатенируем переменную в JS-файлwhile read line; dovariable_str="${line}: \"${!line}\""config_str="${config_str}${variable_str}, "done < $filename# Конец JS-файлаconfig_str="${config_str} };"# Сохраняем файл на дискecho "Creating config-file with content: \"${config_str}\""echo "${config_str}" >> /srv/www/config.env.js# Добавляем <script> в конец всех HTML-файловsed -i '/<\/body><\/html>/ i <script src="http://personeltest.ru/aways/habr.com/confit.env.js"></script>' *.html
Я не большой знаток Bash, вероятно получилось странно. Этот скрипт призван показать общую идею, а не использоваться в проекте напрямую.
Теперь при старте контейнера вместо запуска nginx нужно
выполнить этот скрипт, а потом уже запустить nginx. Заведём точку
входа cmd.sh
, которая сделает это:
#!/bin/bashbash /etc/nginx/generate_env.shnginx -g "daemon off;"
И теперь немного подправим Dockerfile:
FROM node:14.15.0-alpine as buildWORKDIR /app# сборка фронтенд ассетов# ...FROM nginx:1.19-alpine# В стандартной поставке Alpine нет Bash, установим егоRUN apk add bashCOPY ./default.conf /etc/nginx/conf.d/COPY --from=build /app/public /srv/wwwCOPY ./cmd.sh /etc/nginx/cmd.shCOPY ./generate_env.sh /etc/nginx/generate_env.shCOPY ./env.dict /etc/nginx/env.dict EXPOSE 8080CMD ["bash", "/etc/nginx/cmd.sh"]
После этих манипуляций можно передавать на фронтенд любые
параметры через переменные окружения нужно отметить переменную в
env.dict
и передать ее при запуске контейнера.
Живой пример можно посмотреть в этом проекте.
Если внимательно посмотреть на этот вариант, станет понятно, что он почти не отличается от варианта с SSR приведённого в начале статьи. Для удобства можно воспользоваться isomorphic-env-webpack-plugin, и дописать пару скриптов: генерации файла и вставки ссылки на него в HTML.
В этой схеме есть еще один небольшой эдж-кейс обычно в имена файлов с ресурсами добавляют хеш содержимого, чтобы в браузере без проблем кешировать все файлы навсегда по имени. В таком случае нужно немного усложнить скрипт генерации файла с переменными, хешировать содержимое и добавлять результат в имя файла.
Правильная работа с параметрами фронтенд-приложения помогает создавать надежные и удобные в эксплуатации системы. Достаточно отделить конфигурацию от приложения и перенести ее в среду выполнения, чтобы радикально улучшить комфорт членов команды и снизить количество потенциальных ошибок.
А как вы передаёте конфиги в клиентские приложения?
Это простая статическая тестовая страничка, абсолютно ничего интересного.
Чтобы фотограф окупил расходы на одну фотосессию и что-то заработал, он должен за день отснять несколько сотен фотобанкопригодных кадров. Он берет одну модель, несколько смен одежды, визажиста и студию с декорациями. И начинает снимать. Вы покупаете фотографию красивой девушки-туристки, а она же уже была и медсестрой, и стюардессой, и депутатом, и проституткой, и службой техподдержки, и заботливой мамой, и ревнивой подругой, и героиней ваших любимых мемасов
Люди из фотостока полезны, когда вам нужен не конкретный человек, а просто любой какой-то человек. Но если вам подходит любой человек, у вас плохая иллюстрация вы не иллюстрируете, а разбавляете текст. Это значит, что текст слабый, продукт в целом слабый, и единственное, что его украшает это Алена, королева Фотостока.
Важно: в статье будет высказываться субъективное мнение
касательно нескольких сайтов образовательных учреждений. Я не
ставила перед собой цели изучить аудиторию этих сайтов, провести
анализ всех порталов и переделать их дизайн. Это просто моё
мнение.
А ещё здесь будет много картинок.
Сегодня я хочу показать сайт института журналистики и литературного творчества, расположенного в Москве и готовящего бакалавров журналистики. Думаю попробовать себя в журналистике, когда появятся деньги поступить на заочку.
Итак, для посещения сайта вобьём в поисковик аббревиатуру. Нас встречает вот такой креатив:
Результат поискаСпасибо, что включили в метаданные телефон и адрес. Описание больше, чем нужно для поисковой выдачи. Написанное название сайта с маленьких букв выглядит однородно, но вот зачем выделять "об институте" нижним подчёркиванием? Я б ещё поняла, если бы вуз обучал программистов и тестировщиков, но это гуманитарный вуз. Не попадает в специфику.
Далее видим вот это безобразие:
Главный экран сайта ИЖЛТВроде бы использовано два цвета, но исполнение сбивает с толку. Для чего эти синие линии, образующие ромбы? Я пытаюсь отследить их направление взглядом и не понимаю закономерности. Буквы слишком тонкие и сливаются с фоном, из-за этого читать текст трудно. Не хватает контрастности? Я перевела скриншот в чёрно-белый, и тут ситуация с текстом намного лучше:
Чёрно-белый главный экран сайта ИЖЛТДумаю, дело в красном цвете. На его фоне буквы читаются намного хуже, чем на синем.
Обратите внимание, что шрифт у названия вуза, у дополнительной информации и у названия других блоков разительно отличаются. И шрифт с засечками читается намного хуже. Это программа Сдохни или умри, только с чтением текста шрифтом с засечками на странном красном фоне.
Допустим, я интересуюсь поступлением. По навигации в верхнем крае сайта нахожу раздел Информация о поступлении, а потом сразу телефон приёмной комиссии и физический адрес вуза. Ниже в обилии текста нахожу информацию о лицензии, а также программы переподготовки и повышения квалификации. Я хотела написать, что поиск нужной информации занимает больше времени, но, кажется, беру свои слова обратно.
Кстати, стрелки, которые вы видите, не ведут на новые страницы или в низ страницы. Они скрывают дополнительные пункты:
Раскрыла всю дополнительную информациюОставшаяся справа от блока о дипломе стрелка ведёт уже на другую страницу:
Информация о поступлении. Цены и ссылки на правила и условия приёмаНа этой странице уже другая цветовая гамма, но контрастности ещё не хватает. Всё те же беды со шрифтами. А ещё я не понимаю, как этот фон сделан. Напишите в комментариях, если знаете, как.
Плюс стоимость учёбы в вузе и профили видны сразу, не спрятаны в приложениях и нормативных документах. Но из-за отсутствия акцентов и направляющих их сразу не видишь.
Сайт, кстати, не адаптивный. Для сайта образовательного учреждения даже неудивительно:
Внимание на правый край страницыКратко перечислю главные проблемы сайта:
сбивающий с толка фон;
неудачно подобранный оттенок красного цвета на фоне;
неудачная комбинация шрифтов;
отсутствие структуры и направляющих, в том числе из-за сбивающего с толку фона.
Важно, что есть вторая версия сайта, сделанная по требованиям Рособрнадзора. Выглядит она как сайт на конструкторе:
Официальная версия сайта ИЖЛТНа ней же доступен режим чтения, в котором настраивается контрастность и размер шрифта. Здесь мы наблюдаем типичные и знакомые разделы сайта: О сайте, Абитуриентам, Студентам и так далее. И неприятное окошко с чатом в придачу. В этой версии выдержан фирменный синий цвет вуза, а если пролистать к блоку с новостями, то увидим ещё креативы:
Внимание на картинку в третьем блокеКак будто в начало десятых вернулась, спасибо. <sarcasm>
Я посмотрела требования к оформлению сайтов образовательных учреждений. Они ориентированы на размещение нормативных документов. Нормальная практика других организаций размещение только ссылок на документы о стоимости обучения и вступительных испытаниях. В таких случаях даже поиск по сайту бессилен. Требования к оформлению сайтов не говорят размещать только документы говорят выкладывать информацию с приложением её копии. Не считаю правильным оставлять пользователя разбираться с официальными бумагами вуза, пусть главная информация будет выведена в раздел с программами подготовки.
Вот, например, сайт моего колледжа. Он сделан на ucoz, но спасибо админу, что решил не прикладывать одни только документы:
Страница с перечнем документов для поступления во ВГЭКА это сайт МГУ им. Ломоносова. Здесь прекрасно всё: обилие блоков с новостями, статьи о патриотических акциях, которые недавно проводили:
Главная страница МГУ им. ЛомоносоваНа странице с направлениями подготовки слетела вёрстка фильтров:
Страница с направлениями подготовки в МГУ им. ЛомоносоваА это главный экран сайта Вятского государственного университета. Тут ничего и говорить не надо:
Главный экран сайта Вятского универститаКак правило, сайты таких учреждений уже старые, их создавали на ucoz и сейчас поддерживаются инженерами-программистами, получающими максимум тридцать тысяч в месяц за работу. Я проходила практику в школе и видела это своими глазами. Часто на нормальное ведение сайта тупо нет времени, а на его перенос на другой движок нет денег.
Вернёмся к ИЖЛТ. Он поступил правильно и оставил название и перечень вступительных испытаний и стоимость платных мест:
Информация о заочном направлении подготовки журналистовВообще у них есть ещё подбор программы обучения, но в ней нет смысла, поскольку там только бакалавриат журналистики. По сути, вы выбираете только из очного и заочного отделения.
В подвале сайта размещены ссылки на разделы сайта и на ресурсы для студентов. Также размещены ссылки на соцсети в иконках. Последние три ссылки нерабочие и никуда не ведут.
Подвал сайта ИЖЛТЧетвёртая иконка ведёт на страницу лаборатории ИЖЛТ, на которой размещаются материалы из открытых лекций, анонс предстоящих событий и собственное издание. Эта страница сделана в Тильде, и веб-дизайнер даже не заморочился с фавиконом. Но зато оплатили домен!
Внимание на панель вкладокДля начала стоит признать, что сайт образовательного учреждения первое, что видят абитуриенты при поиске подходящего вуза. Именно по нему ищут информацию о поступлении. Навигация по сайту хоть по креативному, хоть по сделанному согласно требованиям должна быть удобной и понятной. На сайтах российских колледжей и вузов привыкли к стандартной горизонтальной панели навигации, которая реализуется в привычном ucoz.
Нельзя просто так взять и приложить одни документы. Опишите в одном-двух абзацах то, к чему они созданы.
Уберите эти чёртовы всплывающие окошки связи. Они бесполезны, если на том конце сайта никого нет. Оставьте для связи почту, физический адрес с графиком работы приёмной комиссии и телефон.
Подберите нормальные шрифты. Для основного текста подойдёт моноширинный без засечек. Не используйте Times New Roman и простигосподи Comic Sans. При подборе гарнитуры для заголовков тоже не пускайтесь во все тяжкие.
Подберите нормальные контрастные цвета, которые будут подходить друг другу, а текст на их фоне прочитают ваши глаза. При подборе цветов используйте цветовой круг. Если текст на фоне не удобочитаемый, попробуйте сначала сделать его белым для тёмного фона или чёрным для светлого, а потом, если это не помогло, подберите другие оттенки фона. Если как фон используете картинку, то затемните и заблюрьте её но слегка, чтобы она оставалась понятной.
Если нет ресурсов перенести сайт на другой движок, подберите или измените нынешний шаблон сайта на ucoz, чтобы интерфейс не отталкивал посетителей.
Лаконичный дизайн испортит патриотическая картинка, поэтому по возможности откажитесь от них. Вместо них подойдут фотографии с мероприятий.
С разрешения директора поищите по знакомым человека на аутсорсе, который за плату поможет поддерживать и обновлять сайт. Ему деньги и опыт, вам лишнее время для работы над другими задачами.