Рендеринг переменной среды в браузер в избыточной производственной сборке act.js, работающей в разных средах
В файле readme http://www. https://github.com/gothinkster/react-redux-realworld-example-app инструкция по редактированию приложения redux realworld.io для редактирования src/agent.js
изменить API_ROOT
указать на другой экземпляр API бэкэнда. Мы хотим настроить вещи так, чтобы API_ROOT
может быть определена переменной среды, которая отличается в разных средах (например, "промежуточная" и "живая"), где мы запускаем производственную сборку.
Мы работаем в контейнерах на openshift kubernetes, следуя принципам 12factor.net, когда код создается один раз, а затем продвигается через среды. Мы можем раскрутить новые среды с помощью одной команды, поэтому мы не хотим, чтобы внутри кода был оператор switch, который называет каждую среду и жестко кодирует бэкэнд API_ROOT
для каждой среды. Вместо этого я хочу иметь возможность запустить существующий образ контейнера производственной сборки в новой среде, где я могу использовать переменную среды, изменить API_ROOT
указать на правильный бэкэнд-API, который содержит данные, с которыми мы хотим проверить.
Я просмотрел несколько разных блогов, ответы на вопросы stackru и официальную документацию. Основная проблема заключается в том, что типичные решения "запекаются" в process.env.API_ROOT
переменная окружения во время сборки, иначе есть переключатель, который жестко кодирует детали всех сред в коде. Ничего из этого не является удовлетворительным, так как мы хотим иметь возможность взять последний стабильный код в существующем контейнере и запустить его в новой среде, используя настроенный там API.
Самое близкое, что у меня есть, это отредактировать код для рендеринга process.env.API_ROOT
в <script>
тег, который устанавливает его на window.API_ROOT
переменная. Затем проверьте, существует ли это, иначе используйте значение по умолчанию при определении const для API_ROOT. Это кажется очень агрессивным и немного хрупким, и мне неясно, где лучше всего отобразить такой тег сценария в образце приложения по адресу https://github.com/gothinkster/react-redux-realworld-example-app
4 ответа
Вопрос № 578 о реакции-создания-приложения имеет хороший ответ. Тибдекс предложил использовать public/env.js
который генерируется с правильными свойствами, то в index.html
добавлять:
<script src="%PUBLIC_URL%/env.js"></script>
Тот env.js
скрипт может установить API_ROOT в окне:
window.env={'API_ROOT':'https://conduit.productionready.io/api'}
А также agent.js
можно проверить на window.env.API_ROOT
иначе по умолчанию:
function apiRoot() {
if( window.env.API_ROOT !== 'undefined') {
return window.env.API_ROOT
}
else {
return 'https://conduit.productionready.io/api'
}
}
const API_ROOT = apiRoot();
Как именно этот файл создается из переменной среды, которую он не описывает, но я смог получить npm start
Команда генерирует это.
ЗатемМурман предложил просто написать экспресс-сервер, который обслуживает /env.js
еще index.html
:
const express = require('express');
const path = require('path');
const app = express();
app.use(express.static(path.join(__dirname, 'build')));
const WINDOW_ENV = "window.env={'API_ROOT':'"+process.env.API_ROOT+"'}\n";
app.get('/env.js', function (req, res) {
res.set('Content-Type', 'application/javascript');
res.send(WINDOW_ENV);
});
app.get('/*', function (req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
app.listen(process.env.PORT);
Чтобы заставить это работать скрипт запуска в package.json
это просто:
"start": "PORT=8080 node server.js",
Тогда все работает. Если API_ROOT
определяется в переменных среды, то server.js
сгенерирует это на window.env
и agent.js
буду использовать это.
Обновление Я установил время кэширования пять минут на env.js с res.setHeader("Cache-Control", "public, max-age=300");
так как настройки редко меняются.
обновление Я прочитал много путаницы вокруг этой темы, и люди, отвечающие на нее по принципу "измените свой рабочий процесс, чтобы соответствовать настройкам по умолчанию инструментов". Идея 12-фактора состоит в том, чтобы использовать рабочий процесс, который признан наилучшей практикой, которой должны следовать инструменты, а не наоборот. В частности, помеченная производственная сборка в контейнере должна настраиваться с помощью переменных среды. Затем это "то же самое", которое отлажено и протестировано, и работает вживую. В этом случае одностраничного приложения требуется, чтобы браузер совершил поездку на сервер для загрузки переменных среды. Этот ответ, IMHO, является простым и простым способом сделать так, чтобы иметь возможность следовать 12-факторным лучшим практикам.
обновление: @mikesparr дает хороший ответ на эту проблему по адресу https://github.com/facebook/create-react-app/issues/982, который заключается в реструктуризации package.json для создания веб-приложения для генерации СПА при запуске. Мы приняли этот подход в качестве тактического обходного пути. Мы используем saas openshift kubernetes, который взимает за память. Создание нашего реагирующего приложения с веб-пакетом требует 1 Гб. Таким образом, при таком подходе к переносу сборки npm в команду запуска контейнера нам нужно выделить 1 Гбайт для каждого запускаемого модуля, что представляет собой значительную сумму дополнительных затрат. Шаг компиляции также медленный, так как это большое приложение. Сборка при каждом запуске приложения замедляет развертывание на много минут. Таким образом, несмотря на то, что он позволяет начать компиляцию приложения в env-вариациях, он далеко не удовлетворителен с точки зрения потребления ресурсов и скорости.
Вы можете заменить переменные окружения прямо в вашем файле index.html, предоставив глобальную переменную ENV. Эту замену необходимо выполнить во время выполнения, чтобы убедиться, что у вас есть переносимый образ, который вы можете запускать в разных средах.
Я создал пример репозитория здесь https://github.com/axelhzf/create-react-app-docker-environment-variables
Взгляните на неизменные веб-приложения!
Это методология, которая создает разделение интересов между index.html
и все остальные статические активы:
- Это лечит
index.html
как манифест развертывания, который содержит все специфические для среды значения.
Это похоже на принятый ответ, включив переменные среды непосредственно в index.html
window.env={'API_ROOT':'https://conduit.productionready.io/api'}
Также необходимо, чтобы ссылка на другие статические ресурсы была уникальной и версионной.
- Он рассматривает пакеты javascript как неизменные ресурсы, которые создаются один раз, публикуются один раз и используются в нескольких средах. Разрешить продвижение активов через среды в производство без изменения или перемещения.
Он соблюдает принципы "сборки, выпуска, запуска" и "настройки" 12factor.
Большим преимуществом этого подхода является то, что он позволяет атомарные живые выпуски, просто публикуя index.html
,
Прошло некоторое время с тех пор, как вопрос был опубликован, но у меня, кажется, есть хороший простой обходной путь.
создайте файл js, который обращается к window.location.hostname;
function getEnvConfig(key){
let host = '';
if (typeof window !== 'undefined') {
host = window.location.hostname;
}
console.log(`Hostname: ${host}`);
if((host.includes('localhost') || host.includes('dev'))) {
return envConfigs.dev[key];
} else if(host.includes('stage')) {
return envConfigs.stage[key];
} else if(host.includes('prod')) {
return envConfigs.prod[key];
}
}
а затем иметь конфигурацию для каждой env в отдельном файле js..
const envConfigs = {
dev: {
NEXT_PUBLIC_API_URL: "https://test.com",
},
stage: {
},
prod: {
},
};
module.exports = {
envConfigs
};
это работает ! и он должен работать в каждой среде.