前言
在开发中,可能会被要求在不同的环境中使用不同的配置,如端口号,请求地址,请求的API接口。
你可能知道 development、test、production 三种环境,分别对应开发环境、线上测试环境和生产环境。
三种环境对应的API
可以为:
- 开发模式 =>
/dev-api/...
,例如:/dev-api/login
- 生产模式 =>
/prod-api/...
,例如:/prod-api/login
- 演示模式 =>
/stage-api/...
,例如:/stage-api/login
一、Vue 前端【Gitee 仓库】
(一)Vue 默认指令与模式
在使用 Vue-cli 创建工程后,package.json
中默认指令如下:
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build"
},
其中
serve
指令,即当你运行npm run serve
时,默认的模式是development
build
指令,即当你运行npm run build
时,默认的模式是production
类似于这样配置
"scripts": {
"serve": "vue-cli-service serve --mode development",
"build": "vue-cli-service build --mode production"
},
所以可以通过添加 --mode env
来指定环境变量。如下,添加test模式:
"scripts": {
"build:test": "vue-cli-service serve --mode test"
},
npm run build:test
(二)环境变量
环境变量是什么?就是一个配置文件,里面提供了一些键值对的数据。
可以解决在不同环境中使用不同的配置,比如项目在开发环境、生产环境、线上测试环境时的请求API
,请求 网址 不一样,这时候就可以通过使用环境变量文件来申明指定环境使用的配置。
更多说明看【Vue-cli 环境变量】文档
在 【vue-admin-template】Github文档中,就是用了三种不同模式下使用不同的API
。如下:
- 开发模式 =>
/dev-api/...
,例如:/dev-api/login
- 生产模式 =>
/prod-api/...
,例如:/prod-api/login
- 演示模式 =>
/stage-api/...
,例如:/stage-api/login
在 Vue-cli 搭建的工程中,可以创建 .env.mode
的文件,配置不同模式下使用的环境变量。
比如,测试模式
// package.json 部分代码
"scripts": {
"build:test": "vue-cli-service build --mode test"
}
// .env.test
ENV = 'test'
VUE_APP_BASE_API = '/test-api'
使用了上述代码后,你在工程中可以使用 process.env.key 来访问你环境变量。
console.log("环境:" + process.env.NODE_ENV);
console.log("base_api:" + process.env.VUE_APP_BASE_API);
--mode mode
指令和 .env.mode
文件名中的 mode
名称一致,在编译时,Vue-cli 就会使用相对应的环境变量文件。
--mode development
=>.env.development
--mode production
=>.env.production
--mode test
=>.env.test
(三)项目查看
你可以分别使用
npm run dev
npm run build
npm run build:test
三个指令来运行工程,之后打开控制台,你会看到,使用三个不同的指令运行,login
的请求地址将都会不一样。
二、NodeJS 后端【Gitee 仓库】
说明:
- 使用 NodeJS + Express 开发服务器,封装请求
API
- 使用
cross-env
添加全局变量,区分模式。- 使用
dotenv
加载.env.mode
文件。
依赖包:cross-env、dotenv、express、nodemon
(一)工程目录
|-- src
| |-- env.js // dotenv 加载 `.env.mode` 文件,并配置到 `process.env` 对象上。
| |-- index.js // 入口文件
|-- .env.development
|-- .env.production
|-- .env.test
|-- package.json
(二)具体实现
1. 代码
// env.js
const path = require("path")
const dotenv = require('dotenv')
const envFileName = path.resolve(__dirname, '../.env.' + process.env.NODE_ENV)
// dotenv 加载指定 .env.{mode} 文件
dotenv.config({ path: envFileName })
// 这里可以删除
console.log(`模式:${process.env.NODE_ENV},`, `BASE_API:${process.env.BASE_API}`);
// index.js
const express = require("express")
// 运行 .env文件
require('./env')
const app = express();
const port = 3000;
// 处理根路径的 GET 请求
app.get('/', (req, res) => {
res.send('Hello, World!');
});
// 处理自定义路由的 GET 请求
const BASE_API = process.env.BASE_API
app.get(`${BASE_API}/user`, (req, res) => {
const user = { id: 1, name: 'John Doe', };
res.json(user);
});
// 启动服务器
app.listen(port, () => {
console.log(`访问: http://localhost:3000${BASE_API}/user`);
});
// .env.development
BASE_API = "/dev-api"
// .env.production
BASE_API = "/prod-api"
// .env.test
BASE_API = "/test-api"
// package.json 部分代码
"scripts": {
"dev": "cross-env NODE_ENV=development nodemon ./src/index.js",
"start:prod": "cross-env NODE_ENV=production node ./src/index.js",
"start:test": "cross-env NODE_ENV=test node ./src/index.js"
}
2. 语言描述
以 start:test
指令为例
- 运行
npm run start:test
指令,值得关注的是cross-env NODE_ENV=test
,这时候会在全局对象process.env
中添加NODE_ENV
属性,值为test
,既process.env.NODE_ENV = "test"
。 - 当执行到
node ./src/index.js
时,会执行到require('./env')
,这样会执行一遍.env.js
文件。 - 在
.env.js
文件中,首先是根据process.env.NODE_ENV
确认.env.{mode}
的文件路径,然后使用dotenv.config({ path: envFileName })
方法让dotenv
加载文件,并解析环境变量。 - 最后将解析的数据注入到全局
process.env
对象上。这里注入了process.env.BASE_API = "/test-api"
。这意味着,你可以在工程中任何地方使用process.env.BASE_API
。
三、TypeScript 后端
由于 TypeScript
需要有一个 tsc
编译阶段,而 tsc
只能将 .ts
文件编译至目标文件夹。这就意味着如果使用 dotenv
模块,加载 .env
环境变量方式时,在使用 tsc
编译原码.ts
文件时,.env
文件将不会直接拷贝到dist
目录。
为此使用,我提供了
- 使用
.ts
环境配置文件的方式,区分不同环境下的不同配置。【Gitee 仓库 master】 - 使用
copyfiles
模块,在tsc
编译完成后,将.env
模块拷贝到dist
目录。【Gitee 仓库 use-env-file 】
两种方式,适应不同的环境,提供不同的 API
。
两种方式的源码都在同一个仓库中,只是放在不同的分支中。
下面是使用 .ts
文件配置环境变量的方式。【Gitee 仓库 master】
(一)工程目录
|-- dist // 编译后,文件目录
|-- src // TS 工程源码目录
| |-- config
| | |-- dev.config.ts // 开发环境 配置文件
| | |-- prod.config.ts // 生产环境 配置文件
| | |-- test.config.ts // 线上测试环境 配置文件
| | |-- index.ts
| |-- index.js // 入口文件
|-- package.json
|-- tsconfig.json
(二)具体实现
// dev.config.ts
const ENV = 'development' // 其他两个文件对应:production、test
const BASE_API = '/dev-api' // 其他两个文件对应:'/prod-api'、'/test-api'
export {
ENV,
BASE_API
}
export default {
ENV,
BASE_API
}
// config/index.ts
import devConfig from './dev.config';
import testConfig from './test.config';
import prodConfig from './prod.config';
let config: any;
switch (process.env.NODE_ENV) {
case 'development':
config = devConfig;
break;
case 'test':
config = testConfig;
break;
case 'production':
config = prodConfig;
break;
default:
throw new Error('Invalid NODE_ENV');
}
mountToEnv(config)
/**
* 挂载到全局 process.env 对象上,这意味着,你在项目任何地方都可以使用 process.env.{key} 来访问你声明的环境变量。
* @param config
*/
function mountToEnv(config: object) {
const values = Object.values(config)
Object.keys(config).forEach((item: string, index: number) => {
process.env[item] = values[index]
})
}
export default config;
// index.ts
import express from 'express';
import config from "./config/index"
const app = express();
const port = 3000;
const BASE_API = config['BASE_API']
app.get('/', (req, res) => {
res.send('Hello,113');
});
app.get(`${BASE_API}/user`, (req, res) => {
res.send('user');
});
// this can delete
console.log(`env: ${config.ENV}, base-api:${config.BASE_API}`);
app.listen(port, () => {
console.log(`访问: http://localhost:3000${BASE_API}/user`);
});
// package.json
"scripts": {
"dev": "cross-env NODE_ENV=development nodemon --watch ./src/**/*.ts --exec ts-node ./src/index.ts",
"build": "rd /s /q dist & tsc",
"start:prod": "cross-env NODE_ENV=production node dist/index.js",
"start:test": "cross-env NODE_ENV=test node dist/index.js"
},