从源码看React环境变量那些事儿

// 文件目录结构
|--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_ENVNODE_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...
    })
    
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容