我已经建立了一个 reactjs 站点并试图让它可以部署。
现在所有的配置都是通过一个 config.js 完成的,它被导入到所有module中。
但是当我构建应用程序时,它会被编译到部署 js 中并且不可配置。
我想要一个单独的文件,系统管理员可以配置特定于其环境的各种设置,例如 API 端点(应用程序可能不在与后端相同的服务器上运行,并且无法访问 DNS )。
有没有办法在react中做到这一点?我也不希望为此使用 3rd 方库。
我已经建立了一个 reactjs 站点并试图让它可以部署。
现在所有的配置都是通过一个 config.js 完成的,它被导入到所有module中。
但是当我构建应用程序时,它会被编译到部署 js 中并且不可配置。
我想要一个单独的文件,系统管理员可以配置特定于其环境的各种设置,例如 API 端点(应用程序可能不在与后端相同的服务器上运行,并且无法访问 DNS )。
有没有办法在react中做到这一点?我也不希望为此使用 3rd 方库。
不确定 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
在卷挂载到已经包含一些文件的现有目录期间省略,则在我们的情况下结果是不利的,因为它将通过将新文件复制到目录中来覆盖现有目录,但删除所有其他先前存在的文件。
我已经设法破解了一个解决方案。
在公共文件夹“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 中使用配置变量或者你叫它什么
...
constructor(props) {
super(props)
console.log(this.props._config)
this.state = {
....
config: this.props._config,
}
}
...
我发现您必须将 config 设置为状态变量,否则它会随机为对象抛出 undefined ,现在只需将 config 向下传递到层次结构即可在您的代码中使用。
我们有一个类似的用例 - “构建一次,随处部署”。以下代码似乎对我们有用:
目录中有一个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;
。
我真的很喜欢你使用配置文件的方法,你可以根据需要替换/更新。我想我可以在有时间的时候试一试——感谢分享你的方法!
我目前处理动态客户端配置的方式基于访问应用程序的 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]);
}
这是我的解决方案。
考虑有一些环境“你知道配置”和其他环境“你不知道配置”,但它们必须由系统管理员配置。
例如 3 个环境:您知道配置的本地和 uat,以及您不知道要设置的配置的生产环境。
所以,我不想为特定环境自动生成一个配置文件,而且这个配置文件必须是可编辑的
我们可以使用一个 config.js 文件来放置一个 json 对象。
所以,首先进入 index.html 并添加 head
<script src="config.js"></script>
这意味着 config.js 文件必须在编译的根文件夹中(与 index.html 相同的位置),这意味着在 react 解决方案的“public”文件夹中。但是我们希望这个 config.js 基于环境是动态的。
考虑在react解决方案根文件夹中创建一个名为“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 文件。在这个 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 install 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 react-app-rewired 安装 rewired
现在,我们需要根据特定环境复制特定的 js 文件。因此,如果我们没有 webpack,请使用 npm install copy-webpack-plugin 安装 copyfile 示例:
在 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 || ''
注意:如果你使用typescript,你需要忽略,因为我们不能将配置作为module导入
因此,现在您可以根据所需的环境使用其中之一启动或构建:
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 会在根应用程序文件夹中生成并且可以编辑(例如由系统管理员)