我已经建立了一个reactjs网站并试图使其可部署。
现在,所有配置都通过导入到所有模块中的config.js完成。
但是当我构建应用程序时,它会被编译到部署js中,并且是不可配置的。
我想拥有一个单独的文件,系统管理员可以根据其环境配置各种设置,例如API端点(该应用程序可能与后端不在同一服务器上运行,并且不会有任何后端访问DNS)。
有没有办法做到这一点?我也不希望为此使用第三方库。
答案 0 :(得分:4)
不确定Linux的方法,但是我经常使用create-react-app(cra),Docker和kubernetes,并最初遇到类似问题。然后,我受到https://github.com/inloop/cra-docker的启发,能够在运行时在Docker和kubernetes中使用create-react-app找到解决配置文件问题的解决方案。以下是我的解决方案中的步骤:
已准备好自定义配置(例如:config.js
)。配置文件中的内容应如下所示:
window.ENV = {
"ENVIRONMENT":"stg",
...other key-value configuration as you'll need
}
通过访问window.ENV.your_configuration_key
,可以在代码中的任何位置访问您的配置(例如,上面的ENVIRONMENT值可从window.ENV.ENVIRONMENT
获得)
在public
目录下,编辑index.html并添加
<script type="text/javascript" src="%PUBLIC_URL%/config.js"></script>
在身体前的头部。并将config.js
放在public
目录下。
解决cra的外部配置的目标是要将config.js
文件放在源目录之外,并将其放在静态public
目录下。如果将配置放在源目录下,则配置将在构建时进行编译,因此您将无法在运行时轻松更改配置(当然,模板也可以,但对我而言并不理想)。请注意,从静态目录提供文件需要一台服务器,但是由于我已经使用nginx
来为我的静态React应用程序提供服务,因此这样做绝对没有问题。
由于我已经在使用nginx
为我的react应用程序提供静态文件,所以我不需要对Dockerfile进行更改即可满足其他config.js
的需求。编译后可在build
目录下找到(因为它被放置在public
目录下)。我的Dockerfile看起来像这样:
# Step 1: Build static react app
FROM node AS builder
# Define working directory and copy source
WORKDIR /app
COPY . .
# Install dependencies and build whatever you have to build
RUN yarn install && yarn build
# Step 2: Run image
FROM nginx
COPY --from=builder /app/build /usr/share/nginx/html
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx # this step is not required, only when you have custom nginx configuration
然后构建您的docker映像:docker build -t [your-docker-image]:latest .
config.js
文件。现在可以轻松完成此操作。如果使用docker运行,则可以使用-v命令替换文件。因此,您的docker运行时命令应类似于以下内容:
docker run --name [app-container-name] -d -p [host_port]:80 \
-v [path_to_config.js_file_in_certain_environment]:/usr/share/nginx/html/config.js \
[your-docker-image]
如果您正在使用kubernetes运行,则可以使用configMap
,volume
,volumeMounts
和subPath
来替换容器中现有目录中的文件。
首先将config.js置于k8s ConfigMap下:
kubectl create configmap [k8s_config_name] --from-file=config.js=[path_to_config.js_file_in_certain_environment]
在k8s部署中挂载configMap:
containers:
...
volumeMounts:
- name: [k8s_config_name_volume]
readOnly: true
mountPath: "/usr/share/nginx/html/config.js"
subPath: "config.js"
volumes:
- name: [k8s_config_name_volume]
configMap:
name: [k8s_config_name]
请注意,mountPath
和subPath
参数对于替换目录中已有文件的目录都是必需的。如果在将卷装入到已经包含一些文件的现有目录的过程中省略subPath
,那么在我们的情况下,结果是不利的,它将通过将新文件复制到该目录中但删除所有其他先前存在的文件来覆盖现有目录。
答案 1 :(得分:3)
我真的很喜欢您的配置文件,您可以根据需要替换/更新它。我想我有空的时候可以试试看-感谢您分享您的方法!
我当前处理动态客户端配置的方式基于正在访问应用程序的URL-我使用它可以在我们的部署管道(开发->测试->过渡->生产)中移动捆绑包,而无需重建捆绑包。
env.js:
const rootUrl = `${location.protocol}//${location.host}`;
deployment-settings
const envs = {
// Production
'https://example.com': {
APP_ENV: 'production',
API_SERVER: 'https://api.example.io',
INTERCOM_APP_ID: 'foo',
},
// Staging
'https://staging.example.com': {
APP_ENV: 'staging',
API_SERVER: 'https://staging-api.example.com',
INTERCOM_APP_ID: 'bar',
},
// Development
'http://localhost:3000': {
APP_ENV: 'development',
API_SERVER: 'http://localhost:4000',
INTERCOM_APP_ID: 'baz',
},
};
if (envs[rootUrl]) {
// Set environment variables based on the URL the app is being accessed from
window.env = envs[rootUrl];
} else {
// Redirect to production if the rootUrl is unknown
window.location.replace(Object.keys(envs)[0]);
}
答案 2 :(得分:2)
我设法破解了一个解决方案。
在公用文件夹“ config.js”中
var config = {
x: 'y',
};
接下来将ReactDOM.render(App / index.js打包成这样的功能
window.RenderApp = (config) => {
ReactDOM.render(<App _config={config}/>, document.getElementById('root'));
}
在index.html中添加这些行,window.RenderApp必须位于末尾,因为它依赖于被导入的bundle.js,该包是由react自动添加的,并且在生产中具有随机名称。
</html>
...
<head>
...
<script type="text/javascript" src="%PUBLIC_URL%/config.js"></script>
...
</head>
...
<body>
...
</body>
<script>
window.RenderApp(config);
</script>
</html>
最后在您的App.js或您曾经使用的名称中使用config变量
...
constructor(props) {
super(props)
console.log(this.props._config)
this.state = {
....
config: this.props._config,
}
}
...
我发现您必须将config设置为状态变量,否则它将为对象随机抛出undefined,现在只需将config传递到层次结构中即可在您的代码中使用。
答案 3 :(得分:2)
我们有一个类似的用例-“一次构建,部署到任何地方”。以下代码似乎对我们有用:
config.js
目录中有public
文件,其代码如下:
const API_URL = '<API_URL>';
index.html
文件包含以下内容:
<script src="/config.js"></script>
<script>
(function (global) {
var config = {
API_URL
};
global.app = {
env: config
};
})(window);
这会将config.js
中的变量添加到global
范围内,并且可以在源代码中的任何地方以const API_ROOT = window.app && window.app.env.API_URL;
的形式进行访问。
答案 4 :(得分:1)
基于此documentation page,react使您的应用能够使用环境变量。您只需要确保您的环境变量以REACT_APP_
开头即可。
此外,您可以在项目根目录的每个环境的文件中声明环境变量
答案 5 :(得分:1)
对于任何寻求简单解决方案的人来说,
在您的公共文件夹中创建一个 config.js 文件
在其中创建和对象例如:
window['getConfig'] = { API_URL:'http://xyz:8080/', PROD_VALUE:'Y' }
然后在您的应用程序(组件/api 调用)中直接将其用作 window['getConfig'].API_URL
由于 config.js 位于公共文件夹中,因此您也可以更改生产环境中的值
答案 6 :(得分:0)
这是我的解决方法。
请考虑在某些环境中“您知道配置”和在其他环境中“您不知道配置”,但是必须由系统管理员来配置。
例如3种环境:您知道配置的本地和uat,以及您不知道要设置的配置的生产环境。
因此,我不希望为特定环境自动生成配置文件,并且该配置文件必须可编辑
我们可以使用config.js文件放置json对象。
因此,首先进入index.html并添加头部
<script src="config.js"></script>
这意味着config.js文件必须位于已编译的根文件夹中(与index.html相同的位置),这意味着在react solution的“ public”文件夹中。 但是我们希望此config.js基于环境是动态的。
考虑在react solution根文件夹中创建一个名为“ config”的文件夹。 在此文件夹中放入3个文件:config_local.js,config_uat.js,config_prod.js
-config_local.js
var Configs = {
API_ENDPOINT':"http://local:88/service"
}
-config_uat.js
var Configs = {
'API_ENDPOINT':"http://uat:88/service"
}
-config_prod.js
var Configs = {
'API_ENDPOINT':""
}
我们需要将这3个文件之一复制到根文件夹中,然后将输出文件重命名为“ config.js”
因此,首先我们需要为每个环境创建一个.env文件。 在此环境文件中,我们只需要设置要复制到根文件夹中的文件名即可。
因此,在react根文件夹中创建.env,.env.uat,.env.local,.env.prod
-。env(默认设置)
REACT_APP_CONFIG_FILE=config_prod.js
-。env.local
REACT_APP_CONFIG_FILE=config_local.js
-。env.uat
REACT_APP_CONFIG_FILE=config_uat.js
-。env.prod
REACT_APP_CONFIG_FILE=config_prod.js
我们需要在package.json中添加/替换脚本对象。 在我们需要安装env-cmd之前(npm安装env-cmd)。这里是一个脚本示例
"scripts": {
"start": "env-cmd -f ./.env react-app-rewired start",
"build": "env-cmd -f ./.env react-app-rewired build",
"test": "env-cmd -f ./.env react-app-rewired test",
"start:local": "env-cmd -f ./.env.local react-app-rewired start",
"build:local": "env-cmd -f ./.env.local react-app-rewired build",
"start:uat": "env-cmd -f ./.env.uat react-app-rewired start",
"build:uat": "env-cmd -f ./.env.uat react-app-rewired build",
"start:prod": "env-cmd -f ./.env.prod react-app-rewired start",
"build:prod": "env-cmd -f ./.env.prod react-app-rewired build"
},
PS:我已经使用了react-app-rewired。我需要它是因为我们需要使用CopyFile插件,但是我无法在webpack中设置它,因为我的应用是使用create-react-app创建的。 因此,如果您还没有webpack文件,请使用npm install rewired重新安装
现在,我们需要根据特定环境复制特定的js文件。 因此,使用npm install copy-webpack-plugin安装copyfile 如果我们没有webpack的示例:
在react根文件夹中创建一个名为“ config-overrides.js”的文件,然后复制以下内容:
const CopyWebpackPlugin = require('copy-webpack-plugin');
let internal_env = process.env.REACT_APP_CONFIG_FILE || 'config_prod.js';
module.exports = function override(config, env) {
if (!config.plugins) {
config.plugins = [];
}
config.plugins.push(
new CopyWebpackPlugin(
[
{
from: 'config/' + internal_env,
to: './config.js'
}
])
);
return config;
};
因此,在我们的react文件中,我们可以使用以下命令读取配置:
//@ts-ignore
const apiEndpoint = Configs.API_ENDPOINT || ''
注意:如果使用打字稿,则需要忽略,因为我们无法将配置作为模块导入
因此,现在您可以根据所需的环境使用其中一种启动或构建:
npm run start
npm run build
npm run start:local
npm run build:local
npm run start:uat
npm run build:uat
npm run start:prod
npm run build:prod
在构建时,特定的config.js会在根应用程序文件夹中生成,并且可以进行编辑(例如,由系统管理员编辑)