同构 React 中的实时环境变量
Live Environment Vars in Isomorphic React
我基于 this repo 中的入门工具包构建了一个松散的同构 React 应用程序。它使用 webpack 构建生产代码。
问题是,我需要将服务器上的一些环境变量的值暴露给浏览器中的客户端代码,而无需重建生产代码。我希望能够更改 env vars 的值,并让它在下一页刷新时影响客户端,而无需重建任何东西。而且我不想为了做到这一点而使测试复杂化。
我找到了一些解决方案,none 其中很棒:
- 使用 webpack 的 DefinePlugin 将某些环境变量的值硬编码到生产代码中。类似于概述的内容
- 构建一个 API 只是为了将 env 变量拉入客户端。
- 写一个特殊的.js文件,在webpack系统之外。该文件将被模板化,以便在将其提供给客户端之前对其进行修改。可能需要将 env 变量值存储在 'window' 或其他东西上的特殊全局变量中。
这些方法的问题:
- 已拒绝。这只是不做我想要的。如果我改变环境变量的值,我需要重建代码。
- 不必要的复杂。我不需要这个 API 做任何其他事情。整个 API 只是为了提供 2 或 3 个很少更改的值?需要复杂性来确保在加载时尽快从 API 中提取值。
- 最接近,但有点恶心。如果可以避免,我真的不想走出 webpack/React/Flux 系统。在 window 对象上创建特殊的全局变量会起作用,但会引入使用这些全局变量的测试 components/stores/actions 的复杂性。
我过去已经完成了 2 和 3,但从未真正对这些解决方案在智力上感到满意。
有什么建议吗?看起来这应该是一个 common/solved 问题。也许我只是想多了,3 是正确的选择。
样板使用express,你可以使用express'local向客户端公开服务器环境变量。
var serverVariable = 'this is a server variable'
app.get('/somelink', function (req, res, next) {
res.locals.data = {
FluxStore: { serverlocal: serverVariable }
}
next()
})
然后您通过 React.renderToString 传递本地,这将由客户端上的 FluxStore 挑选。另一种方法,您可以使用像 falcor 这样的数据获取 api,它可以由客户端 Action Store 通过 falcor-http-datasource 选择,您不需要使用 falcor-express 和 falcor 构建它的 falcor express local -路由器
所以我提出的解决方案相当简单。我只是写了一行 javascript 来将值保存到脚本标记内的本地存储中。然后在应用程序启动时从我的通量存储中读取该本地存储。
这是添加到 index.html 的相关标签:
<script type="text/javascript"><%= config %></script>
这是我在提供之前使用模板拼接成 index.html 的字符串:
let configJs = 'localStorage.setItem(\'ADMIN_API\', JSON.stringify(\'' + process.env.ADMIN_API + '/v3/' + '\'));';
const html = template({config: configJs});
res.status(200).send(html);
然后我在应用程序启动后用这个阅读它:
import ls from 'local-storage';
....
let api = ls.get('ADMIN_API');
这在 window
中使用 window 全局变量来传递值,但为您提供了一个通用接口来访问浏览器和节点上的值。
在publicEnv.js
中:
// Env variable to push to the client. Careful on what you put here!
const publicEnv = [
'API_URL',
'FACEBOOK_APP_ID',
'GA_ID'
];
// These 2 lines make sure we pick the value in the right place on node and the browser
const isBrowser = typeof window !== 'undefined';
const base = (isBrowser ? window.__ENV__ : process.env) || {};
const env = {};
for (const v of publicEnv) {
env[v] = base[v];
}
export default env;
在页面的 HTML 模板文件中,我有:
import publicEnv from 'publicEnv.js';
...
<script>
window.__ENV__ = ${stringify(publicEnv)};
// Other things you need here...
window.__INITIAL_STATE__ = ${stringify(initialState)};
</script>
所以现在我可以通过以下方式获取前端和后端环境变量的值:
import publicEnv from 'publicEnv.js';
...
console.log("Google Analytic code is", publicEnv.GA_ID);
希望能帮到你
我基于 this repo 中的入门工具包构建了一个松散的同构 React 应用程序。它使用 webpack 构建生产代码。
问题是,我需要将服务器上的一些环境变量的值暴露给浏览器中的客户端代码,而无需重建生产代码。我希望能够更改 env vars 的值,并让它在下一页刷新时影响客户端,而无需重建任何东西。而且我不想为了做到这一点而使测试复杂化。
我找到了一些解决方案,none 其中很棒:
- 使用 webpack 的 DefinePlugin 将某些环境变量的值硬编码到生产代码中。类似于概述的内容
- 构建一个 API 只是为了将 env 变量拉入客户端。
- 写一个特殊的.js文件,在webpack系统之外。该文件将被模板化,以便在将其提供给客户端之前对其进行修改。可能需要将 env 变量值存储在 'window' 或其他东西上的特殊全局变量中。
这些方法的问题:
- 已拒绝。这只是不做我想要的。如果我改变环境变量的值,我需要重建代码。
- 不必要的复杂。我不需要这个 API 做任何其他事情。整个 API 只是为了提供 2 或 3 个很少更改的值?需要复杂性来确保在加载时尽快从 API 中提取值。
- 最接近,但有点恶心。如果可以避免,我真的不想走出 webpack/React/Flux 系统。在 window 对象上创建特殊的全局变量会起作用,但会引入使用这些全局变量的测试 components/stores/actions 的复杂性。
我过去已经完成了 2 和 3,但从未真正对这些解决方案在智力上感到满意。
有什么建议吗?看起来这应该是一个 common/solved 问题。也许我只是想多了,3 是正确的选择。
样板使用express,你可以使用express'local向客户端公开服务器环境变量。
var serverVariable = 'this is a server variable'
app.get('/somelink', function (req, res, next) {
res.locals.data = {
FluxStore: { serverlocal: serverVariable }
}
next()
})
然后您通过 React.renderToString 传递本地,这将由客户端上的 FluxStore 挑选。另一种方法,您可以使用像 falcor 这样的数据获取 api,它可以由客户端 Action Store 通过 falcor-http-datasource 选择,您不需要使用 falcor-express 和 falcor 构建它的 falcor express local -路由器
所以我提出的解决方案相当简单。我只是写了一行 javascript 来将值保存到脚本标记内的本地存储中。然后在应用程序启动时从我的通量存储中读取该本地存储。
这是添加到 index.html 的相关标签:
<script type="text/javascript"><%= config %></script>
这是我在提供之前使用模板拼接成 index.html 的字符串:
let configJs = 'localStorage.setItem(\'ADMIN_API\', JSON.stringify(\'' + process.env.ADMIN_API + '/v3/' + '\'));';
const html = template({config: configJs});
res.status(200).send(html);
然后我在应用程序启动后用这个阅读它:
import ls from 'local-storage';
....
let api = ls.get('ADMIN_API');
这在 window
中使用 window 全局变量来传递值,但为您提供了一个通用接口来访问浏览器和节点上的值。
在publicEnv.js
中:
// Env variable to push to the client. Careful on what you put here!
const publicEnv = [
'API_URL',
'FACEBOOK_APP_ID',
'GA_ID'
];
// These 2 lines make sure we pick the value in the right place on node and the browser
const isBrowser = typeof window !== 'undefined';
const base = (isBrowser ? window.__ENV__ : process.env) || {};
const env = {};
for (const v of publicEnv) {
env[v] = base[v];
}
export default env;
在页面的 HTML 模板文件中,我有:
import publicEnv from 'publicEnv.js';
...
<script>
window.__ENV__ = ${stringify(publicEnv)};
// Other things you need here...
window.__INITIAL_STATE__ = ${stringify(initialState)};
</script>
所以现在我可以通过以下方式获取前端和后端环境变量的值:
import publicEnv from 'publicEnv.js';
...
console.log("Google Analytic code is", publicEnv.GA_ID);
希望能帮到你