mirror of
https://github.com/zhigang1992/create-react-app.git
synced 2026-06-15 09:58:21 +08:00
Adding namespaced environment variables to DefinePlugin under REACT_APP_ (#342)
This commit is contained in:
committed by
Dan Abramov
parent
6e18b2a069
commit
ffe6b2f904
24
config/env.js
Normal file
24
config/env.js
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
|
||||
// injected into the application via DefinePlugin in Webpack configuration.
|
||||
|
||||
var REACT_APP = /^REACT_APP_/i;
|
||||
var NODE_ENV = JSON.stringify(process.env.NODE_ENV || 'development');
|
||||
|
||||
module.exports = Object
|
||||
.keys(process.env)
|
||||
.filter(key => REACT_APP.test(key))
|
||||
.reduce((env, key) => {
|
||||
env['process.env.' + key] = JSON.stringify(process.env[key]);
|
||||
return env;
|
||||
}, {
|
||||
'process.env.NODE_ENV': NODE_ENV
|
||||
});
|
||||
@@ -14,6 +14,7 @@ var HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
var CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
|
||||
var WatchMissingNodeModulesPlugin = require('../scripts/utils/WatchMissingNodeModulesPlugin');
|
||||
var paths = require('./paths');
|
||||
var env = require('./env');
|
||||
|
||||
// This is the development configuration.
|
||||
// It is focused on developer experience and fast rebuilds.
|
||||
@@ -170,9 +171,9 @@ module.exports = {
|
||||
template: paths.appHtml,
|
||||
favicon: paths.appFavicon,
|
||||
}),
|
||||
// Makes the environment available to the JS code, for example:
|
||||
// Makes some environment variables available to the JS code, for example:
|
||||
// if (process.env.NODE_ENV === 'development') { ... }. See `env.js`.
|
||||
new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"development"' }),
|
||||
new webpack.DefinePlugin(env),
|
||||
// Note: only CSS is currently hot reloaded
|
||||
// This is necessary to emit hot updates (currently CSS only):
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
|
||||
@@ -14,6 +14,13 @@ var HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
var ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||
var url = require('url');
|
||||
var paths = require('./paths');
|
||||
var env = require('./env');
|
||||
|
||||
// Assert this just to be safe.
|
||||
// Development builds of React are slow and not intended for production.
|
||||
if (env['process.env.NODE_ENV'] !== '"production"') {
|
||||
throw new Error('Production builds must have NODE_ENV=production.');
|
||||
}
|
||||
|
||||
// We use "homepage" field to infer "public path" at which the app is served.
|
||||
// Webpack needs to know it to put the right <script> hrefs into HTML even in
|
||||
@@ -188,11 +195,11 @@ module.exports = {
|
||||
minifyURLs: true
|
||||
}
|
||||
}),
|
||||
// Makes the environment available to the JS code, for example:
|
||||
// Makes some environment variables available to the JS code, for example:
|
||||
// if (process.env.NODE_ENV === 'production') { ... }. See `env.js`.
|
||||
// It is absolutely essential that NODE_ENV was set to production here.
|
||||
// Otherwise React will be compiled in the very slow development mode.
|
||||
new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"production"' }),
|
||||
new webpack.DefinePlugin(env),
|
||||
// This helps ensure the builds are consistent if source hasn't changed:
|
||||
new webpack.optimize.OccurrenceOrderPlugin(),
|
||||
// Try to dedupe duplicated modules, if any:
|
||||
|
||||
@@ -34,6 +34,7 @@ prompt(
|
||||
path.join('config', 'flow', 'file.js.flow'),
|
||||
path.join('config', 'eslint.js'),
|
||||
path.join('config', 'paths.js'),
|
||||
path.join('config', 'env.js'),
|
||||
path.join('config', 'polyfills.js'),
|
||||
path.join('config', 'webpack.config.dev.js'),
|
||||
path.join('config', 'webpack.config.prod.js'),
|
||||
|
||||
@@ -18,7 +18,9 @@ You can find the most recent version of this guide [here](https://github.com/fac
|
||||
- [Adding Images and Fonts](#adding-images-and-fonts)
|
||||
- [Adding Bootstrap](#adding-bootstrap)
|
||||
- [Adding Flow](#adding-flow)
|
||||
- [Adding Custom Environment Variables](#adding-custom-environment-variables)
|
||||
- [Integrating with a Node Backend](#integrating-with-a-node-backend)
|
||||
- [Proxying API Requests in Development](#proxying-api-requests-in-development)
|
||||
- [Deployment](#deployment)
|
||||
- [Now](#now)
|
||||
- [Heroku](#heroku)
|
||||
@@ -179,6 +181,7 @@ export default Button; // Don’t forget to use export default!
|
||||
|
||||
### `DangerButton.js`
|
||||
|
||||
|
||||
```js
|
||||
import React, { Component } from 'react';
|
||||
import Button from './Button'; // Import a component from another file
|
||||
@@ -370,7 +373,7 @@ esproposal.class_static_fields=enable
|
||||
esproposal.class_instance_fields=enable
|
||||
|
||||
module.name_mapper='^\(.*\)\.css$' -> 'react-scripts/config/flow/css'
|
||||
module.name_mapper='^\(.*\)\.\(jpg\|png\|gif\|eot\|otf\|svg\|ttf\|woff\|woff2\|mp4\|webm\)$' -> 'react-scripts/config/flow/file'
|
||||
module.name_mapper='^\(.*\)\.\(jpg\|png\|gif\|eot\|otf\|webp\|svg\|ttf\|woff\|woff2\|mp4\|webm\)$' -> 'react-scripts/config/flow/file'
|
||||
|
||||
suppress_type=$FlowIssue
|
||||
suppress_type=$FlowFixMe
|
||||
@@ -382,24 +385,141 @@ If you later `eject`, you’ll need to replace `react-scripts` references with t
|
||||
|
||||
```ini
|
||||
module.name_mapper='^\(.*\)\.css$' -> '<PROJECT_ROOT>/config/flow/css'
|
||||
module.name_mapper='^\(.*\)\.\(jpg\|png\|gif\|eot\|otf\|svg\|ttf\|woff\|woff2\|mp4\|webm\)$' -> '<PROJECT_ROOT>/config/flow/file'
|
||||
module.name_mapper='^\(.*\)\.\(jpg\|png\|gif\|eot\|otf\|webp\|svg\|ttf\|woff\|woff2\|mp4\|webm\)$' -> '<PROJECT_ROOT>/config/flow/file'
|
||||
```
|
||||
|
||||
We will consider integrating more tightly with Flow in the future so that you don’t have to do this.
|
||||
|
||||
## Adding Custom Environment Variables
|
||||
|
||||
>Note: this feature is available with `react-scripts@0.2.3` and higher.
|
||||
|
||||
Your project can consume variables declared in your environment as if they were declared locally in your JS files. By
|
||||
default you will have `NODE_ENV` defined for you, and any other environment variables starting with
|
||||
`REACT_APP_`. These environment variables will be defined for you on `process.env`. For example, having an environment
|
||||
variable named `REACT_APP_SECRET_CODE` will be exposed in your JS as `process.env.REACT_APP_SECRET_CODE`, in addition
|
||||
to `process.env.NODE_ENV`.
|
||||
|
||||
These environment variables can be useful for displaying information conditionally based on where the project is
|
||||
deployed or consuming sensitive data that lives outside of version control.
|
||||
|
||||
First, you need to have environment variables defined, which can vary between OSes. For example, let's say you wanted to
|
||||
consume a secret defined in the environment inside a `<form>`:
|
||||
|
||||
```jsx
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<small>You are running this application in <b>{process.env.NODE_ENV}</b> mode.</small>
|
||||
<form>
|
||||
<input type="hidden" defaultValue={process.env.REACT_APP_SECRET_CODE} />
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
The above form is looking for a variable called `REACT_APP_SECRET_CODE` from the environment. In order to consume this
|
||||
value, we need to have it defined in the environment:
|
||||
|
||||
### Windows (cmd.exe)
|
||||
|
||||
```cmd
|
||||
set REACT_APP_SECRET_CODE=abcdef&&npm start
|
||||
```
|
||||
|
||||
(Note: the lack of whitespace is intentional.)
|
||||
|
||||
### Linux, OS X (Bash)
|
||||
|
||||
```bash
|
||||
REACT_APP_SECRET_CODE=abcdef npm start
|
||||
```
|
||||
|
||||
> Note: Defining environment variables in this manner is temporary for the life of the shell session. Setting
|
||||
permanent environment variables is outside the scope of these docs.
|
||||
|
||||
With our environment variable defined, we start the app and consume the values. Remember that the `NODE_ENV`
|
||||
variable will be set for you automatically. When you load the app in the browser and inspect the `<input>`, you will see
|
||||
its value set to `abcdef`, and the bold text will show the environment provided when using `npm start`:
|
||||
|
||||
```html
|
||||
<div>
|
||||
<small>You are running this application in <b>development</b> mode.</small>
|
||||
<form>
|
||||
<input type="hidden" value="abcdef" />
|
||||
</form>
|
||||
</div>
|
||||
```
|
||||
|
||||
Having access to the `NODE_ENV` is also useful for performing actions conditionally:
|
||||
|
||||
```js
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
analytics.disable();
|
||||
}
|
||||
```
|
||||
|
||||
## Integrating with a Node Backend
|
||||
|
||||
Check out [this tutorial](https://www.fullstackreact.com/articles/using-create-react-app-with-a-server/) for instructions on integrating an app with a Node backend running on another port, and using `fetch()` to access it. You can find the companion GitHub repository [here](https://github.com/fullstackreact/food-lookup-demo).
|
||||
|
||||
## Proxying API Requests in Development
|
||||
|
||||
>Note: this feature is available with `react-scripts@0.2.3` and higher.
|
||||
|
||||
People often serve the front-end React app from the same host and port as their backend implementation.
|
||||
For example, a production setup might look like this after the app is deployed:
|
||||
|
||||
```
|
||||
/ - static server returns index.html with React app
|
||||
/todos - static server returns index.html with React app
|
||||
/api/todos - server handles any /api/* requests using the backend implementation
|
||||
```
|
||||
|
||||
Such setup is **not** required. However, if you **do** have a setup like this, it is convenient to write requests like `fetch('/api/todos')` without worrying about redirecting them to another host or port during development.
|
||||
|
||||
To tell the development server to proxy any unknown requests to your API server in development, add a `proxy` field to your `package.json`, for example:
|
||||
|
||||
```js
|
||||
"proxy": "http://localhost:4000",
|
||||
```
|
||||
|
||||
This way, when you `fetch('/api/todos')` in development, the development server will recognize that it’s not a static asset, and will proxy your request to `http://localhost:4000/api/todos` as a fallback.
|
||||
|
||||
Conveniently, this avoids [CORS issues](http://stackoverflow.com/questions/21854516/understanding-ajax-cors-and-security-considerations) and error messages like this in development:
|
||||
|
||||
```
|
||||
Fetch API cannot load http://localhost:4000/api/todos. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
|
||||
```
|
||||
|
||||
Keep in mind that `proxy` only has effect in development (with `npm start`), and it is up to you to ensure that URLs like `/api/todos` point to the right thing in production. You don’t have to use the `/api` prefix. Any unrecognized request will be redirected to the specified `proxy`.
|
||||
|
||||
Currently the `proxy` option only handles HTTP requests, and it won’t proxy WebSocket connections.
|
||||
If the `proxy` option is **not** flexible enough for you, alternatively you can:
|
||||
|
||||
* Enable CORS on your server ([here’s how to do it for Express](http://enable-cors.org/server_expressjs.html)).
|
||||
* Use [environment variables](#adding-custom-environment-variables) to inject the right server host and port into your app.
|
||||
|
||||
## Deployment
|
||||
|
||||
By default, Create React App produces a build assuming your app is hosted at the server root.
|
||||
To override this, specify the `homepage` in your `package.json`, for example:
|
||||
|
||||
```js
|
||||
"homepage": "http://mywebsite.com/relativepath",
|
||||
```
|
||||
|
||||
This will let Create React App correctly infer the root path to use in the generated HTML file.
|
||||
|
||||
### Now
|
||||
|
||||
See [this example](https://github.com/xkawi/create-react-app-now) for a zero-configuration single-command deployment with [now](https://zeit.co/now).
|
||||
|
||||
### Heroku
|
||||
|
||||
Use the [Heroku Buildpack for create-react-app](https://github.com/mars/create-react-app-buildpack).
|
||||
Use the [Heroku Buildpack for Create React App](https://github.com/mars/create-react-app-buildpack).
|
||||
You can find instructions in [Deploying React with Zero Configuration](https://blog.heroku.com/deploying-react-with-zero-configuration).
|
||||
|
||||
### Surge
|
||||
|
||||
@@ -426,17 +546,15 @@ Note that in order to support routers that use html5 `pushState` API, you may wa
|
||||
|
||||
>Note: this feature is available with `react-scripts@0.2.0` and higher.
|
||||
|
||||
First, open your `package.json` and add a `homepage` field.
|
||||
It could look like this:
|
||||
Open your `package.json` and add a `homepage` field:
|
||||
|
||||
```js
|
||||
{
|
||||
"name": "my-app",
|
||||
"homepage": "http://myusername.github.io/my-app",
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
**The above step is important!**
|
||||
Create React App uses the `homepage` field to determine the root URL in the built HTML file.
|
||||
|
||||
Now, whenever you run `npm run build`, you will see a cheat sheet with a sequence of commands to deploy to GitHub pages:
|
||||
|
||||
```sh
|
||||
@@ -451,7 +569,7 @@ git checkout -
|
||||
|
||||
You may copy and paste them, or put them into a custom shell script. You may also customize them for another hosting provider.
|
||||
|
||||
Note that GitHub Pages doesn't support routers that use the HTML5 `pushState` history API under the hood (for example, React Router using `browserHistory`). This is becasue when there is a fresh page load for a url like `http://user.github.io/todomvc/todos/42`, where `/todos/42` is a frontend route, the GitHub Pages server returns 404 because it knows nothing of `/todos/42`. If you want to add a router to a project hosted on GitHub Pages, here are a couple of solutions:
|
||||
Note that GitHub Pages doesn't support routers that use the HTML5 `pushState` history API under the hood (for example, React Router using `browserHistory`). This is because when there is a fresh page load for a url like `http://user.github.io/todomvc/todos/42`, where `/todos/42` is a frontend route, the GitHub Pages server returns 404 because it knows nothing of `/todos/42`. If you want to add a router to a project hosted on GitHub Pages, here are a couple of solutions:
|
||||
* You could switch from using HTML5 history API to routing with hashes. If you use React Router, you can switch to `hashHistory` for this effect, but the URL will be longer and more verbose (for example, `http://user.github.io/todomvc/#/todos/42?_k=yknaj`). [Read more](https://github.com/reactjs/react-router/blob/master/docs/guides/Histories.md#histories) about different history implementations in React Router.
|
||||
* Alternatively, you can use a trick to teach GitHub Pages to handle 404 by redirecting to your `index.html` page with a special redirect parameter. You would need to add a `404.html` file with the redirection code to the `build` folder before deploying your project, and you’ll need to add code handling the redirect parameter to `index.html`. You can find a detailed explanation of this technique [in this guide](https://github.com/rafrex/spa-github-pages).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user