preface

As we all know, there is a process global variable in Node whose env attribute gives us access to environment variables in the system. As shown in the figure below, only partial variable names are truncated.

The magic of the process. The env

You recently noticed that process.env is also accessible in browsers, as shown below

The environment variable used in vue-element-admin

VUE_APP_BASE_API is defined in the. Env * file, and variables starting with VUE_APP are read into environment variables at project startup, which is mandatory by vuE-CLI tools. “If you want to use my tools, you have to follow my rules 😈”

That’s a very simple one, and we’ll find out later

Behind this was the interaction of a series of toolchains (Webpack + Dotenv + Webpack DefinePlugin), which was my first understanding of front-end engineering.

After reading the article, you will:

  • rightvue-cliloading.envDocuments have a better understanding
  • Preliminary understanding of front-end engineering
  • In multiplayer development,.envHow to use it properly

Let’s focus on Dotenv

dotenv

Dotenv is a zero-dependency module that loads environment variables from the.env file into process.env. Dotenv’s default policy is to skip loading an environment variable if it exists in a.evn file that is identical to the one in the system. Remember this: 😎

Let’s try it out by initializing the project first

npm init -y
Copy the code

Then install Dotenv

npm i dotenv -D
Copy the code

Create an. Env file in the root directory of the project (package.json directory)

The contents of the SRC /main.js file are loaded by calling the config() function

const dotenv = require('dotenv')
dotenv.config()
console.log(`name=> ${process.env.NAME}, male=> ${process.env.SEX}`)
Copy the code

The results are as follows

config(options)You can pass in an object argument with the following interface

interfaceDotenvConfigOptions { path? :string; encoding? :string; debug? :boolean;
  /** * Whether to overwrite existing environment variables in the system with values in the.env file * Default is false */override? :boolean;
}
Copy the code

Config () can be called multiple times, which means that multiple files can be loaded simultaneously.

Such as:

// Variables defined in all three files are loaded in
config({path: 'path1'})
config({path: 'path2'})
config({path: 'path3'})
Copy the code

Some of you might say, “Isn’t this still running on Node?”

Don’t worry, next we invite big Brother —

Webpack is used in conjunction with Dotenv

First install webpack, webpack-CLI, code to run in the browser, and then install an HTML-webpack-plugin

npm i webpack webpack-cli html-webpack-plugin -D
Copy the code

A brief configuration of WebPack

const {resolve} = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  mode: 'development'.entry: './src/main.js'.output: {
    filename: '[name].js'.path: resolve(__dirname, 'dist'),
    clean: true,},plugins: [
    new HtmlWebpackPlugin({
      title: 'Custom load env file'}})]Copy the code

Modify the contents of the main.js file

- const dotenv = require('dotenv')
- dotenv.config({
-})
- console.log(`name=> ${process.env.NAME}, male=> ${process.env.SEX}`)

+ const p = document.createElement('p')
+ p.extContent = 'You are now in the development environment, welcome: Developer Roman'
+ p.style.cssText = `
+ text-align: center;
+ color: teal;
+  font-size: 2em;`
+ document.body.appendChild(p)
Copy the code

Although we are currently hard-coding, we can later make the development and Production environments display different content by reading environment variables. Dotenv runs in the Node environment, but our code runs in the browser. How do we connect them?

It occurred to me that the Webpack packaging phase also takes place on Node, so the following idea was born

  1. usedotenvloading.envEnvironment variables in
  2. Environment variables are read during the packaging phase
  3. Somehow merge the results into the packaged results

For example:

// webpack.config.js

+ const dotenv = require('dotenv')

+ dotenv.config()

+ const stringifiedEnv = JSON.stringify(process.env)

module.exports = {
+ mode: process.env.NODE_ENV || 'development',
 // = = = = = = = = = = = = = = = = = = = = = omit = = = = = = = = = = = = = = = = = = = = = = = =
  plugins: [
    new HtmlWebpackPlugin({
- title: 'Customize loading env file'
+ title: stringifiedEnv}})]Copy the code
// main.js

const p = document.createElement('p')
+ const process = {}
+ process.env = JSON.parse(document.title)
+ document.title = 'custom load env'
-p.extContent = 'You are now in the development environment, welcome: Developer Roman'
+ p.extContent = 'You are now in ${process.env.node_env}, welcome you: ${process.env.name}'
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = to omit the = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Copy the code

Open dist/index.html to run the effect

It’s not a good example, but we can be sure that the idea works.

DefinePlugin appearance

One of the questions we now face is, how do we gracefully inject environment variables into the packaging results? There is a built-in plugin in Webpack, DefinePlugin, that solves this problem perfectly.

DefinePlugin lets you replace variables in your code with other values or expressions at compile time. Simply put, it does text substitution, hard-coding values or expressions into code.

For example, IF I want to define a global variable, I can access the age variable directly in any other file

// webpack.config.js
const age = 3; It can be'roman'This string is inlined to the package resultnew DefinePlugin({
  age
})

// anyOther.js
console.log(age) / / 3
Copy the code

That’s because when you pack,DefinePluginMade a text replacement to put ouragereplace3

Note: Since this is a direct text substitution, if you want to replace a string such as’ Roman ‘, you must write it as’ Roman ‘.

Go ahead and modify webpack.config.js to inline our environment variables into the code

// = = = = = = = = = = = = = = = = = = = = = = omit = = = = = = = = = = = = = = = = = = = = = = = =
+ const {DefinePlugin} = require('webpack')

// = = = = = = = = = = = = = = = = = = = = = = omit = = = = = = = = = = = = = = = = = = = = = = = =

module.exports = {
  // = = = = = = = = = = = = = = = = = = = = = = omit = = = = = = = = = = = = = = = = = = = = = = = =
  plugins: [
    new HtmlWebpackPlugin({
- title: env
+ title: 'Custom load env file'
    }),
+ new DefinePlugin({
+ 'process.env' = stringifiedEnv
+})]}Copy the code

Since you can now access process.env directly, delete the extra code in SRC /main.js

- const process = {}
- process.env = JSON.parse(document.title)
// = = = = = = = = = = = = = = = = = = = = = = omit = = = = = = = = = = = = = = = = = = = = = = = =
+ console.log(process.env)
Copy the code

The result is the same, but we are now able to access process.env successfully

However, this lack of environment variables seems a bit redundant, so let’s try to expose only some of the environment variables.

Customize your own env reading rules

Because there are too many environment variables in the system, and some environment variables also involve privacy issues. The system now needs to be implemented, exposing only the environment variables prefixed with ROMAN_APP. To isolate configuration from code, create env.js files in the project root directory

const dotenv = require("dotenv");

const ROMAN_APP = /^ROMAN_APP/i;

dotenv.config();

const raw = Object.keys(process.env)
  // Iterate over environment variables that conform only to regular expressions
  .filter((key) = > ROMAN_APP.test(key))
  .reduce(
    (prev, key) = > {
      prev[key] = process.env[key];
      return prev;
    },
    {
      // There is usually a NODE_ENV environment variable
      NODE_ENV: process.env.NODE_ENV || "development"});const stringifiedEnv = JSON.stringify(raw)

module.exports = {
  raw,
  stringifiedEnv
}
Copy the code

Env.js is simple: use regular expressions to match environment variables prefixed with ROMAN_APP, and import stringifiedEnv in webpack.config.js

now.envTry writing something else in the file at 😏

When you look at the results after the build, the only environment variables that are exposed are those prefixed with ROMAN_APP. Process.env.name is undefined because the code in SRC /main.js is not changed

So far, we have achieved “load on demand” environment variables, the following is a brief introduction to the development of multiple people, how to use properly.envfile

Multiplayer development mode.envuse

Axios, for example, typically creates an instance by reading environment variables

const request = axios.create({
  baseURL: process.env.VUE_APP_BASE_API, 
  timeout: 5000 
  // Some other configurations...
})
Copy the code

This VUE_APP_BASE_API is clearly configured in the. Env file. In multiplayer development, each front end connects to a different back end, so this VUE_APP_BASE_API must be different. This is a recipe for error in multiplayer.

You can actually leave the.env file as it was, write another.env file, and then you can do whatever you want to do with the.env file, and then just submit the original.env file when you submit your code.

So we have the following convention

Env.[mode]. Local # is loaded only in the specified mode, prior to. Env.[mode], but ignored by Git. Env takes precedence over. Env, but is ignored by Git. Env # is loaded in all environmentsCopy the code

Where mode corresponds to production/development

Let’s improve the functionality of env.js

const dotenv = require("dotenv");
+ const { resolve } = require("path");
+ const {existsSync} = require('fs')

const ROMAN_APP = /^ROMAN_APP/i;
+ const mode = process.env.NODE_ENV || 'development';


+ const envPath = resolve(__dirname, '.env')
+ const pathList = [
+ `${envPath}.${mode}.local`,
+ `${envPath}.${mode}`,
+ `${envPath}.local`,
+ `${env}`
+]

+ pathList.forEach(path => {
+ if (existsSync(path)) {
+ dotenv.config({path})
+}
+})Const raw = object.keys (process.env) // Traverses environment variables that match regular expressions only. Filter ((key) => roman_app.test (key)).reduce((prev, key) => { prev[key] = process.env[key]; return prev; }, {// There is usually a NODE_ENV environment variable- NODE_ENV: process.env.NODE_ENV || 'development'
+ NODE_ENV: mode}); const stringifiedEnv = JSON.stringify(raw); module.exports = { raw, stringifiedEnv, }Copy the code

Make a copy of the.env file, rename it.env.loacl, and guess what NODE_ENV ends up being?

Did you get that right? As for why to leave friends to ponder (in fact, the answer has been revealed in the text. 🤐)

Finally, if you want to commit code without ignoring the local. Env file, add.local to the.gitignore file

dist
node_modules
.local
Copy the code

The demo address