// 文件目录结构
|--config
|--jest
|--env.js
...
|--paths.js
|--node_modules
|--public
|--scripts
|--build.js
|--start.js
|--test.js
|--src
|--package.json
使用第三方库 dotenv、dotenv-expand
-
dotenv解析文件赋值到process.env,默认解析.env文件。也可添加配置去解析其他文件
const path = require("path"); const envFile = path.join(__dirname, ".env.local") dotenv({path: envFile});
-
Dotenv-expand
默认dotenv不能解析文件里的变量,必须得是字符串才行,dotenv-expand 扩展了dotenv的功能
可在目标文件里通过{} 引用变量
// .env.local NODE_ENV = "production" VUE_APP_NAME = "vue app" SUNSET = $REAC_APP_NAME
使用规则
React中规定,要使用process.env上的变量,可以在本地新建.env、.env.development、.env.production、.env.local 等文件,在文件里定义以REACT_APP
开头的变量。然后就可以在项目中使用 process.env.xxx
等环境变量。
从源码中看环境变量的定义过程
-
当我们执行npm start 时,在
scripts --> start.js
文件头部一开始就挂载了两个变量
BABEL_ENV
、NODE_ENV
// Do this as the first thing so that any code reading it knows the right env. process.env.BABEL_ENV = 'development'; process.env.NODE_ENV = 'development';
-
然后进入到
config --> env.js
文件首先这里定义 dotenvFiles ,是一个包含几个文件名的数组,这几个文件名就是上面提到的react规定的几个定义变量的文件(.env 、.env.local 、.env.development ...)
其中paths.dotenv可以在
config --> paths.js`中找到,就是项目根目录下的.env文件,
其余几种都是基于.env的名字出来的。const NODE_ENV = process.env.NODE_ENV; if (!NODE_ENV) { throw new Error( 'The NODE_ENV environment variable is required but was not specified.' ); } // https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use const dotenvFiles = [ `${paths.dotenv}.${NODE_ENV}.local`, `${paths.dotenv}.${NODE_ENV}`, // Don't include `.env.local` for `test` environment // since normally you expect tests to produce the same // results for everyone NODE_ENV !== 'test' && `${paths.dotenv}.local`, paths.dotenv, ].filter(Boolean);
这里遍历上面的dotenvFiles,并用dotenv,dotenv-expand 分别绑定变量到 process.env上。
执行完下面这段代码后就可以在process.env上看到自己在.env等文件中定义的变量。
到目前为止这些变量都只是在node环境中可见,在浏览器中是找不到的接下来通过webpack.DefinePlugin把变量注入到浏览器环境。
// Load environment variables from .env* files. Suppress warnings using silent // if this file is missing. dotenv will never modify any environment variables // that have already been set. Variable expansion is supported in .env files. // https://github.com/motdotla/dotenv // https://github.com/motdotla/dotenv-expand dotenvFiles.forEach(dotenvFile => { if (fs.existsSync(dotenvFile)) { require('dotenv-expand')( require('dotenv').config({ path: dotenvFile, }) ); } });
-
在把变量注入到浏览器环境之前,react还做了一层过滤,在
config --> env.js
里定义了一个/^REACT_APP_/i
正则,这个函数就是只抽取 变量名是REACT_APP_
开头的变量作为客户端环境变量const REACT_APP = /^REACT_APP_/i; function getClientEnvironment(publicUrl) { const raw = Object.keys(process.env) .filter(key => REACT_APP.test(key)) .reduce( (env, key) => { env[key] = process.env[key]; return env; }, { // Useful for determining whether we’re running in production mode. // Most importantly, it switches React into the correct mode. NODE_ENV: process.env.NODE_ENV || 'development', // Useful for resolving the correct path to static assets in `public`. // For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />. // This should only be used as an escape hatch. Normally you would put // images into the `src` and `import` them in code to get their paths. PUBLIC_URL: publicUrl, // We support configuring the sockjs pathname during development. // These settings let a developer run multiple simultaneous projects. // They are used as the connection `hostname`, `pathname` and `port` // in webpackHotDevClient. They are used as the `sockHost`, `sockPath` // and `sockPort` options in webpack-dev-server. WDS_SOCKET_HOST: process.env.WDS_SOCKET_HOST, WDS_SOCKET_PATH: process.env.WDS_SOCKET_PATH, WDS_SOCKET_PORT: process.env.WDS_SOCKET_PORT, } ); // Stringify all values so we can feed into webpack DefinePlugin const stringified = { 'process.env': Object.keys(raw).reduce((env, key) => { env[key] = JSON.stringify(raw[key]); return env; }, {}), }; return { raw, stringified }; } module.exports = getClientEnvironment;
-
接下来打开
config --> webpack.config.js
,在plugins
配置里new webpack.DefinePlugin(env.stringified),
这一步就是把最终过滤出来的变量,注入到客户端环境了。
最后放上一段webpack的部分文件介绍。
DefinePlugin
DefinePlugin
允许创建一个在编译时可以配置的全局常量。这可能会对开发模式和发布模式的构建允许不同的行为非常有用。如果在开发构建中,而不在发布构建中执行日志记录,则可以使用全局常量来决定是否记录日志。这就是DefinePlugin
的用处,设置它,就可以忘记开发和发布构建的规则。new webpack.DefinePlugin({ // Definitions... })