Tl;dr:使用 AJAX 并在反应上下文或全局变量中进行配置。
详细解答:
确实如您所说,使用npm run build构建应用程序后,环境变量变为硬连线,无法更改。
create-react-app的官方声明是不支持一次构建部署多次的原则。来自https://create-react-app.dev/docs/adding-custom-environment-variables/:
环境变量在构建时嵌入。由于 Create React App 生成一个静态 HTML/CSS/JS 包,它不可能在运行时读取它们。
不过,实现原理是有方法的,只是稍微复杂一点。这个想法是,您需要在运行时从外部源获取变量的值,例如阿贾克斯。更详细地说,可能的解决方案可能是(但不限于)以下:
1。服务器端占位符替换
这是create-react-app 在https://create-react-app.dev/docs/title-and-meta-tags/#injecting-data-from-the-server-into-the-page 中提出的解决方案,引入了一个自定义占位符,并在呈现给客户端之前将其替换为服务器上的数据。
<!doctype html>
<html lang="en">
<head>
<script>
window.SERVER_DATA = __SERVER_DATA__;
</script>
虽然这可行,但它会带来很大的开销,因为它将整个后端实现留给您。根据您的技术堆栈,这可能非常容易实现,也可能非常复杂。
2。分配变量值的动态<script>
https://www.cotyhamilton.com/build-once-deploy-anywhere-for-react-applications/ 中提出的解决方案利用了 javascript 的动态特性。在动态下载的config.js 文件中,为变量分配了一个值。在其余的 React 代码中,变量被读取和使用。您可以随时更改config.js 文件,无需重新编译react 应用。
// public/config.js
const apiUrl = 'localhost:1337';
const env = 'development';
<!-- public/index.html -->
<script src="%PUBLIC_URL%/config.js"></script>
<script>
window.config = { apiUrl, env };
</script>
主要的缺点是它不支持 TypeScript,你的 IDE 或 linter 可能会抱怨 apiUrl 和 env 没有定义。尤其是在较大的项目中,这种方法可能难以维护。
3。使用 AJAX 进行动态配置,支持 TypeScript
基于第二个解决方案,本文https://profinit.eu/en/blog/build-once-deploy-many-in-react-dynamic-configuration-properties/ 非常详细地描述了如何使用create-react-app 最好地实现一次构建部署多次 原则以及优缺点。
它建议使用 AJAX 将动态配置下载为 JSON。主要的警告是确保在一些代码轮胎使用它之前下载动态配置。在 React 生命周期的上下文中,有两种方法可以实现这一点。
3.1 全局变量
从globalConfigUrl 下载动态配置 JSON,将其存储在全局变量中,然后才渲染 React 应用程序。 TypeScript 中的示例:
// index.tsx:
import axios from "axios";
import React, {ReactElement} from "react";
import App from "./App";
import {globalConfig, globalConfigUrl} from "./configuration/config";
axios.get(globalConfigUrl)
.then((response) => {
globalConfig.config = response.data; // THIS IS THE IMPORTANT LINE
return <App />;
})
.catch(e => {
return <p style={{color: "red", textAlign: "center"}}>Error while fetching global config</p>;
})
.then((reactElement: ReactElement) => {
ReactDOM.render(
reactElement,
document.getElementById("root")
);
});
完整的工作示例:https://codesandbox.io/s/build-once-deploy-many-global-config-object-dvpzr
3.2.反应上下文
使用包含配置的反应上下文提供程序包装您的<App> 组件(使用undefined 或一些默认值)。首次呈现 App 时获取配置,然后将其值保存到上下文中。 React 会处理剩下的事情并传播值的变化!
完整的工作示例:https://codesandbox.io/s/build-once-deploy-many-react-context-7lk7g
基本思想是这样的。查看上面的文章/工作示例以了解所有详细信息:
// App.tsx
import {useConfig} from "./configuration/useConfig";
// ... in the method:
const { setConfig } = useConfig(); // the `useConfig` is a custom hook, wrapping a React context. See the full working example for all details
useEffect(() => {
axios
.get(dynamicConfigUrl)
.then((response) => {
setConfig(response.data);
})
}, [setConfig]);