[Разработка веб-сайтов, JavaScript, Nginx] Солидные фронтенды: конфигурация
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Манифест 12-факторных приложений внес весомый вклад в процесс разработки и эксплуатации веб-приложений, но это по-большей части коснулось бекендов, и обошло стороной фронтенды. Большенство пунктов манифеста или не применимы к фронтендам, или выполняются сами собой, но с номером 3 — конфигурация — есть вопросики.В оригинальном манифесте сказано: «Сохраняйте конфигурацию в среде выполнения». На практике это означает, что конфигурацию нельзя хранить внутри исходного кода или финального артефакта. Ее нужно передавать в приложение по время запуска. У этого правила есть практические применения, вот парочка:
- Приложение в разных окружениях должно обращаться к разным бекендам. На продакшене — к продакшн API, на стейдженге — к стейджинг API, а при запуске интеграционных тестов — к специальному мок-серверу.
- Для е2е тестов нужно снижать время ожидания реакции пользователя. Например, на если на сайте после 10 минут бездействия что-то происходит, то для тестового сценария можно уменьшить этот интервал до минуты.
SSRЕсли фронтенд-приложение содержит в себе SSR, то задача становится чуточку легче. Конфигурацию передают как переменные окружения в приложение на сервере, при рендере она попадает в ответ клиенту как глобальные переменные, объявленные в <script> в самом начале страницы. На клиенте же достаточно эти переменные подхватывать из глобальной области видимости и использовать.Недавно мы в Авиасейлс делали приложение для серверного рендеринга кусочков сайта и столкнулись с этой задачей. Результат мой тиммейт заопенсорсил — isomorphic-env-webpack-plugin.
Прекрасный Next.js умеет так из коробки, ничего специально делать не нужно.
CSRДругое дело, если приложение — набор статических файлов. Переменные окружения нельзя передать на клиент, ведь мы не контролируем сервер, а все файлы уже заранее сгенерированы и лежат на диске. В таком случае, на этот фактор часто забивают.Два самых популярных способа хранить конфигурацию фронтенд приложения с клиентским рендерингом:
- Положить к исходным текстам – завести в коде файлик 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 build
WORKDIR /app
# сборка фронтенд ассетов
# ...
FROM nginx:1.19-alpine
COPY ./default.conf.template /etc/nginx/templates/default.conf.template
COPY --from=build /app/public /srv/www
EXPOSE 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_URL
GOOGLE_CLIENT_ID
Теперь простым Bash-скриптом generate_env.sh будем доставать значения из окружения и складывать в JS-файл:
#!/bin/bash
filename='/etc/nginx/env.dict'
# Начало JS-файла
config_str="window._env_ = { "
# Конкатенируем переменную в JS-файл
while read line; do
variable_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="/confit.env.js"></script>' *.html
Я не большой знаток Bash, вероятно получилось странно. Этот скрипт призван показать общую идею, а не использоваться в проекте напрямую.
Теперь при старте контейнера вместо запуска nginx нужно выполнить этот скрипт, а потом уже запустить nginx. Заведём точку входа cmd.sh, которая сделает это:
#!/bin/bash
bash /etc/nginx/generate_env.sh
nginx -g "daemon off;"
И теперь немного подправим Dockerfile:
FROM node:14.15.0-alpine as build
WORKDIR /app
# сборка фронтенд ассетов
# ...
FROM nginx:1.19-alpine
# В стандартной поставке Alpine нет Bash, установим его
RUN apk add bash
COPY ./default.conf /etc/nginx/conf.d/
COPY --from=build /app/public /srv/www
COPY ./cmd.sh /etc/nginx/cmd.sh
COPY ./generate_env.sh /etc/nginx/generate_env.sh
COPY ./env.dict /etc/nginx/env.dict
EXPOSE 8080
CMD ["bash", "/etc/nginx/cmd.sh"]
После этих манипуляций можно передавать на фронтенд любые параметры через переменные окружения — нужно отметить переменную в env.dict и передать ее при запуске контейнера.
Живой пример можно посмотреть в этом проекте.
Если внимательно посмотреть на этот вариант, станет понятно, что он почти не отличается от варианта с SSR приведённого в начале статьи. Для удобства можно воспользоваться isomorphic-env-webpack-plugin, и дописать пару скриптов: генерации файла и вставки ссылки на него в HTML.В этой схеме есть еще один небольшой эдж-кейс — обычно в имена файлов с ресурсами добавляют хеш содержимого, чтобы в браузере без проблем кешировать все файлы навсегда по имени. В таком случае нужно немного усложнить скрипт генерации файла с переменными, хешировать содержимое и добавлять результат в имя файла. ЗаключениеПравильная работа с параметрами фронтенд-приложения помогает создавать надежные и удобные в эксплуатации системы. Достаточно отделить конфигурацию от приложения и перенести ее в среду выполнения, чтобы радикально улучшить комфорт членов команды и снизить количество потенциальных ошибок.А как вы передаёте конфиги в клиентские приложения?
===========
Источник:
habr.com
===========
Похожие новости:
- [Информационная безопасность, JavaScript, История IT, Софт, IT-компании] Новая фишинговая атака использует азбуку Морзе для сокрытия вредоносных URL-адресов
- [Разработка веб-сайтов, PHP, Laravel] Laravel–Дайджест (25 января – 7 февраля 2021)
- [Разработка веб-сайтов, PHP, Программирование] Enum в PHP 8.1 — для чего нужен enum, и как реализован в PHP
- [Разработка веб-сайтов, Программирование] Custom Elements из Angular в Angular
- [Firefox, Разработка веб-сайтов, Open source, Google Chrome, Браузеры] Невменяемый, необъятный масштаб браузеров (перевод)
- [JavaScript, SvelteJS] Svelte + Redux + Redux-saga
- [Настройка Linux, Разработка веб-сайтов, CSS, JavaScript] Просто вертикальный монитор не значит, что я на телефоне (перевод)
- [JavaScript, Монетизация мобильных приложений, Голосовые интерфейсы] Как создавать навыки для виртуальных ассистентов Салют, выйти на многомиллионную аудиторию Сбера и выиграть 2,5 млн руб?
- [JavaScript, ReactJS] createRef, setRef, useRef и зачем нужен current в ref
- [Разработка веб-сайтов, *nix, Разработка под e-commerce, Управление e-commerce] Интернет-магазин «на минималках»
Теги для поиска: #_razrabotka_vebsajtov (Разработка веб-сайтов), #_javascript, #_nginx, #_frontend (фронтенд), #_konfiguratsija (конфигурация), #_sajt (сайт), #_razrabotka_vebsajtov (
Разработка веб-сайтов
), #_javascript, #_nginx
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 11:20
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Манифест 12-факторных приложений внес весомый вклад в процесс разработки и эксплуатации веб-приложений, но это по-большей части коснулось бекендов, и обошло стороной фронтенды. Большенство пунктов манифеста или не применимы к фронтендам, или выполняются сами собой, но с номером 3 — конфигурация — есть вопросики.В оригинальном манифесте сказано: «Сохраняйте конфигурацию в среде выполнения». На практике это означает, что конфигурацию нельзя хранить внутри исходного кода или финального артефакта. Ее нужно передавать в приложение по время запуска. У этого правила есть практические применения, вот парочка:
Прекрасный Next.js умеет так из коробки, ничего специально делать не нужно.
Дальше инструкция специфична для nginx в Docker-контейнере
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; } } FROM node:14.15.0-alpine as build
WORKDIR /app # сборка фронтенд ассетов # ... FROM nginx:1.19-alpine COPY ./default.conf.template /etc/nginx/templates/default.conf.template COPY --from=build /app/public /srv/www EXPOSE 8080 Живой пример можно посмотреть в этом проекте.
window.__ENV__ = {
USER_API_URL: 'https://user.my-service.io/', AUTH_API_URL: 'https://auth.other-service.io/', }; Дальше инструкция специфична для nginx в Docker-контейнере
BACK_URL
GOOGLE_CLIENT_ID #!/bin/bash
filename='/etc/nginx/env.dict' # Начало JS-файла config_str="window._env_ = { " # Конкатенируем переменную в JS-файл while read line; do variable_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="/confit.env.js"></script>' *.html Я не большой знаток Bash, вероятно получилось странно. Этот скрипт призван показать общую идею, а не использоваться в проекте напрямую.
#!/bin/bash
bash /etc/nginx/generate_env.sh nginx -g "daemon off;" FROM node:14.15.0-alpine as build
WORKDIR /app # сборка фронтенд ассетов # ... FROM nginx:1.19-alpine # В стандартной поставке Alpine нет Bash, установим его RUN apk add bash COPY ./default.conf /etc/nginx/conf.d/ COPY --from=build /app/public /srv/www COPY ./cmd.sh /etc/nginx/cmd.sh COPY ./generate_env.sh /etc/nginx/generate_env.sh COPY ./env.dict /etc/nginx/env.dict EXPOSE 8080 CMD ["bash", "/etc/nginx/cmd.sh"] Живой пример можно посмотреть в этом проекте.
=========== Источник: habr.com =========== Похожие новости:
Разработка веб-сайтов ), #_javascript, #_nginx |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 11:20
Часовой пояс: UTC + 5