用于部署后设置的 React 配置文件

IT技术 reactjs react-native
2021-04-18 18:04:38

我已经建立了一个 reactjs 站点并试图让它可以部署。

现在所有的配置都是通过一个 config.js 完成的,它被导入到所有module中。

但是当我构建应用程序时,它会被编译到部署 js 中并且不可配置。

我想要一个单独的文件,系统管理员可以配置特定于其环境的各种设置,例如 API 端点(应用程序可能不在与后端相同的服务器上运行,并且无法访问 DNS )。

有没有办法在react中做到这一点?我也不希望为此使用 3rd 方库。

6个回答

不确定 linux 方法,但我经常使用 create-react-app (cra)、Docker 和 kubernetes,最初也遇到了类似的问题。然后我受到了https://github.com/inloop/cra-docker 的启发,并且能够在运行时在 Docker 和 kubernetes 中使用 create-react-app 找到配置文件问题的解决方案。以下是我的解决方案中的步骤:

  1. config.js准备好您的自定义配置(例如:)。配置文件中的内容应如下所示:

     window.ENV = {
         "ENVIRONMENT":"stg",
         ...other key-value configuration as you'll need
     }
    

    通过访问可以在代码中的任何位置访问您的配置window.ENV.your_configuration_key(例如:上面的 ENVIRONMENT 值位于window.ENV.ENVIRONMENT

  2. public目录下,编辑 index.html 并添加

     <script type="text/javascript" src="%PUBLIC_URL%/config.js"></script>
    

    在你的头前身体。并放在目录config.jspublic

    您解决 cra 外部配置的目标是将您的config.js文件放在源目录之外并将其放在静态public目录下。如果您将配置放在源目录下,配置将在构建期间编译,因此您将无法在运行时轻松更改配置(嗯,模板也可以工作,但对我来说并不理想)。请注意,从静态目录提供文件需要一个服务器,但由于我已经在使用nginx任何方式为我的静态 React 应用程序提供服务,因此我完全可以做到这一点。

  3. 由于我已经使用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 .

  4. 最后但同样重要的是,您需要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运行,您可以通过使用替代在现有目录中的文件的容器下configMapvolumevolumeMountssubPath

    • 首先将你的 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]
      

    请注意,mountPathsubPath参数对于替换已存在一些文件的目录下的文件是必需的。如果subPath在卷挂载到已经包含一些文件的现有目录期间省略,则在我们的情况下结果是不利的,因为它将通过将新文件复制到目录中来覆盖现有目录,但删除所有其他先前存在的文件。

不幸的是,这在更改PUBLIC_URL.
2021-06-05 18:04:38
我正在寻找一种不需要更改 Dockerfile 基本映像的解决方案。谢谢这个!
2021-06-09 18:04:38
在K8但在情况下,其工作的罚款docker run或者docker-compose它不工作?
2021-06-16 18:04:38

我已经设法破解了一个解决方案。

在公共文件夹“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.jsglobal作用域中,并且可以在源代码中的任何位置访问,如 : 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]);
}
@Mikael 你能帮助我们如何扩展这个配置以在 react package.json 文件中使用吗?我的意思是,根据不同的环境应用构建和启动命令?
2021-05-23 18:04:38
谢谢!但我遇到的问题是我正在构建的内容将被部署到一个气隙网络中,我不知道系统管理员希望如何部署它。所以我想要一种更“linux”的方法,只在文件系统上的某个地方有一个配置文件,他们可以在不了解代码的情况下进行配置。
2021-05-25 18:04:38

这是我的解决方案。

考虑有一些环境“你知道配置”和其他环境“你不知道配置”,但它们必须由系统管理员配置。

例如 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 会在根应用程序文件夹中生成并且可以编辑(例如由系统管理员)