reactjs - 在 ReactJS 应用程序中,如何将全局环境中的环境变量替换为我的 index.html 文件?
问题描述
我有一个 ReactJS 17 应用程序,我想将全局环境中的环境变量替换到我的src/index.html
文件中。.env
但是,正如这个答案所暗示的那样,我们没有使用文件 - Create React App: using environment variables in index.html。这是src/index.html
文件...
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
...
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="512x512">
<script src="%REACT_APP_THIRD_PARTY_URL%"></script>
</head>
<body>
在我的 shell 中,我可以在我的全局环境中看到 env var
$ echo $REACT_APP_THIRD_PARTY_URL
http://thirdpartyurl.com
我在开发模式下运行我的应用程序,使用安装npm i
和运行npm run start
。但是,上面的替换从未进行过,并且页面正在呈现
<script src="%REACT_APP_THIRD_PARTY_URL%"></script>
编辑:
这是一个开发环境,所以热重新加载等等。这是我在 package.json 中“开始”的方式
"start": "webpack-dev-server --open --config webpack.development.js"
编辑 2:不确定这是否重要,但这是在我的 webpack 文件中......
我的 package.json ...
{
"name": "my_app",
"version": "1.0.0",
"description": "My desc",
"main": "index.js",
"scripts": {
"test": "jest",
"test:watch": "jest --watchAll",
"test:coverage": "jest --coverage",
"build": "webpack --config webpack.production.js",
"start": "webpack-dev-server --open --config webpack.development.js",
...
这是 webpack.development.js 文件...
require('dotenv').config();
const { merge } = require('webpack-merge');
const common = require('./webpack.common');
const WEBPACK_WATCH_POLLING = process.env.WEBPACK_WATCH_POLLING === '1';
module.exports = merge(common, {
mode: 'development',
devtool: 'inline-source-map',
devServer: {
contentBase: './dist',
publicPath: '/',
historyApiFallback: true,
disableHostCheck: true,
hot: true,
host: '0.0.0.0',
port: 8080,
},
output: {
publicPath: '/',
},
// This is needed for docker-on-windows.
watchOptions: !WEBPACK_WATCH_POLLING
? undefined
: {
poll: 1000,
},
});
这是我的 webpack.common 文件...
module.exports = {
entry: './src/index.tsx',
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
resolve: {
extensions: ['.tsx', '.ts', '.jsx', '.js', '.json', '.css', '.scss'],
alias: {
'@': path.resolve('src'),
'@sb': path.resolve('.storybook'),
'@static': path.resolve('static'),
common: path.resolve('src/components/common'),
},
},
module: {
rules: [
{
test: /\.(js|ts)x?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
babelrc: true,
},
},
},
{
test: /\.html$/,
use: [
{
loader: 'html-loader',
},
],
},
{
test: /\.(scss|css)$/i,
use: ['style-loader', 'css-loader', 'sass-loader'],
},
{
test: /\.(jpe?g|gif|png)$/,
use: {
loader: 'url-loader',
},
},
{
test: /\.svg$/,
use: {
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'icons/',
},
},
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
use: {
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/',
},
},
},
],
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: './src/index.html',
REACT_APP_THIRD_PARTY_URL: "http://google.com"
//filename: './index.html',
}),
new CopyWebpackPlugin({
patterns: [
{ from: 'src/manifest.json' },
{ from: 'static/**/*.{png,svg,ico}', flatten: true },
],
}),
new WorkboxWebpackPlugin.InjectManifest({
swSrc: './src/service-worker.js',
}),
new EnvironmentPlugin([
'NODE_ENV',
'REACT_APP_THIRD_PARTY_URL',
]),
],
};
最后,我的 src/index.tsx 文件...
import './monitor';
import * as serviceWorker from './sw-registration';
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/app/App';
import './i18n.js';
ReactDOM.render(
<React.StrictMode>
<React.Suspense fallback={<div>Loading...</div>}>
<App />
</React.Suspense>
</React.StrictMode>,
document.getElementById('root')
);
serviceWorker.register();
解决方案
根据您的环境和构建工具,有几种可能的方法。
我想对您来说最方便的方法是在构建时将它们提供给您的项目。
- 您可以
index.html
动态生成文件(通过使用模板引擎或为您执行作业的简单脚本)并通过从环境中读取所需的变量来手动附加所需的变量。 - 或者更优雅的解决方案 - 将此步骤集成到您的构建工具中。如果您正在使用
webpack
构建您的包,您可以使用为您HtmlWebpackPlugin
构建您的index.html
文件,并为您提供所需的所有环境数据。
为此,请创建一个您将用作模板的简单文件.ejs
或文件:.html
<!DOCTYPE html>
<html>
<head>
...
<script src="<%= htmlWebpackPlugin.options.thirdPartyData.url %>"></script>
</head>
<body></body>
</html>
然后,在您的webpack.config.js
设置中,HtmlWebpackPlugin
如下所示:
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
...
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: '<PATH_TO_YOUR_TEMPLATE>',
thirdPartyData: {
url: process.env.<YOUR_ENV_VARIABLE>
}
})
]
};
Webpack
将从环境中获取变量(通过node
),并将index.html
根据您的模板文件为您构建。您可以向插件提供任意数据,然后您可以从htmlWebpackPlugin.options
属性中读取模板。您可以在此处了解有关HtmlWebpackPlugin 的更多信息。
请注意,为了更改代码本身中的环境变量,您必须通过 进行重建webpack
,这是可以理解的。
此解决方案不需要您使用create-react-app
或.env
文件,也不需要服务器/服务器渲染来为您完成这项工作。请注意,它也完全在反应应用程序的上下文之外工作(您不在反应应用程序本身,您直接在.html
标记上工作)。
npm scripts
另一种可能的方法是通过使用标志来提供环境变量,--env
但这对您来说不方便集成,因为它需要一些额外的工作。
评论后编辑:
这是一个最小的示例沙箱。由于它执行脚本的方式,提供的代码将无法在代码和框本身内部工作,但如果您在您的机器上复制提供的示例,它会做它应该做的事情。为了测试它,你只需要index.html
、 the webpack.config.json
、 thepackage.json
和一个 dummy index.js
。为简洁起见,我删除了与反应相关的代码。
只需下载代码和框示例,运行npm install
,然后npm start
.
然后,您可以将该方法迁移到您的特定代码库。
编辑 2:
好的,我发现了问题。您html-loader
会干扰HtmlWebpackPlugin
并且不会替换模板中的变量。如果您html-loader
在 webpack.config 中注释掉转换工作,您会注意到。因此,最简单的解决方案将是:
- 创建一个新
index.html.ejs
文件并将其放在您的index.html
. 这是一个真正合适的ejs
模板。把你的当前内容放在index.html
那里:
index.html.ejs
:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="512x512">
<script src="<%= htmlWebpackPlugin.options.REACT_APP_THIRD_PARTY_URL %>"></script>
...
</head>
<body>
...
</body>
</html>
- 在您的
webpack.common.js
观点中,配置中的template
属性HtmlWebpackPlugin
会导致该模板:
new HtmlWebpackPlugin({
template: './src/index.html.ejs',
REACT_APP_THIRD_PARTY_URL: "http://google.com"
})
此时您甚至可以完全删除index.html
它,因为它将由HtmlWebpackPlugin
插件生成。
通过这种方式,您可以保留您的html-loader
转换(如果您确实需要它)并获得插值的 html 文件。
推荐阅读
- mysql - 一张桌子,而不是两张,有一些空列
- html - 在显示为 flex 时保持 div 中的图像填充并保持纵横比
- keras - LSTM 预测如何合并多个自相关
- css - 如何更改 primeNg 树中树节点图标的颜色?
- python - Python,允许将单个字符串和字符串列表传递给函数
- spring-boot - Spring Boot Gemfire 服务器配置
- python - Python - 在数字字符串(不是整数)列表中填充 0 - ZFILL
- javascript - 想要阵列到阵列
- dictionary - 与地图的其他元素的模式匹配
- php - 我无法使用 save() 方法 laravel 5.5 保存更改