6

我基于这个 repo中的入门套件松散地构建了一个同构 React 应用程序。它使用 webpack 来构建生产代码。

问题是,我需要将服务器上的一些环境变量的值暴露给浏览器中的客户端代码,而不需要重新构建生产代码。我希望能够更改环境变量的值,并使其在客户端刷新下一页时产生效果,而无需重建任何内容。而且我不想为了做到这一点而使测试复杂化。

我找到了一些解决方案,但都不是很好:

  1. 使用 webpack 的 DefinePlugin 将某些环境变量的值硬编码到生产代码中。类似于此处概述的内容
  2. 构建一个 API 只是为了将环境变量拉入客户端。
  3. 在 webpack 系统之外编写一个特殊的 .js 文件。该文件将被模板化,以便在提供给客户端之前对其进行修改。可能需要将环境变量值存储在“窗口”或其他东西上的特殊全局变量中。

这些方法的问题:

  1. 被拒绝。这只是不符合我的要求。如果我更改了 env 变量的值,我需要重新构建代码。
  2. 不必要的复杂。我不需要这个 API 来做其他事情。一个完整的 API 只是为了提供 2 或 3 个很少更改的值?需要复杂性以确保在加载时尽快从 API 中提取值。
  3. 最接近,但有点恶心。如果可以避免的话,我真的不想离开 webpack/React/Flux 系统。在窗口对象上创建特殊的全局变量是可行的,但会增加测试使用这些全局变量的组件/存储/操作的复杂性。

过去我已经完成了 2 和 3,并且从未对这些解决方案真正感到满意。

有什么建议么?似乎这应该是一个常见/已解决的问题。也许我只是想多了,3是要走的路。

4

3 回答 3

2

样板使用 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,它可以通过 falcor-http-datasource 由客户端 Action Store 选择,您不需要使用 falcor-express 和 falcor-router 构建 falcor 的 express local

于 2015-09-25T12:48:52.943 回答
0

这使用一个窗口全局变量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);

我希望它可以帮助。

于 2017-01-20T00:58:46.033 回答
0

So the solution that I came up with is fairly simple. I just write a one-liner of javascript to save the value to local storage inside a script tag. Then read that local storage from my flux store when the app starts.

This is the relevant tag added to the index.html:

<script type="text/javascript"><%= config %></script>

This is the string that I splice into index.html using templating, before serving it:

let configJs = 'localStorage.setItem(\'ADMIN_API\', JSON.stringify(\'' + process.env.ADMIN_API + '/v3/' + '\'));';
const html = template({config: configJs});
res.status(200).send(html);

Then I read it with this, once the app starts:

import ls from 'local-storage';
....
let api = ls.get('ADMIN_API');
于 2015-09-28T17:07:32.973 回答