無(wú)標(biāo)題文章

# 構(gòu)建基礎(chǔ)篇 3:env 文件與環(huán)境設(shè)置

在實(shí)際項(xiàng)目的開(kāi)發(fā)中莲兢,我們一般會(huì)經(jīng)歷項(xiàng)目的開(kāi)發(fā)階段盲泛、測(cè)試階段和最終上線階段,每一個(gè)階段對(duì)于項(xiàng)目代碼的要求可能都不盡相同,那么我們?nèi)绾文軌蛴稳杏杏嗟脑诓煌A段下使我們的項(xiàng)目呈現(xiàn)不同的效果央勒,使用不同的功能呢?這里就需要引入**環(huán)境**的概念澳化。

一般一個(gè)項(xiàng)目都會(huì)有以下 3 種環(huán)境:

*? 開(kāi)發(fā)環(huán)境(開(kāi)發(fā)階段崔步,本地開(kāi)發(fā)版本,一般會(huì)使用一些調(diào)試工具或額外的輔助功能)

*? 測(cè)試環(huán)境(測(cè)試階段缎谷,上線前版本井濒,除了一些 bug 的修復(fù),基本不會(huì)和上線版本有很大差別)

*? 生產(chǎn)環(huán)境(上線階段列林,正式對(duì)外發(fā)布的版本瑞你,一般會(huì)進(jìn)行優(yōu)化,關(guān)掉錯(cuò)誤報(bào)告)

作為一名開(kāi)發(fā)人員希痴,我們可能需要針對(duì)每一種環(huán)境編寫(xiě)一些不同的代碼并且保證這些代碼運(yùn)行在正確的環(huán)境中者甲,那么我們應(yīng)該如何在代碼中判斷項(xiàng)目所處的環(huán)境同時(shí)執(zhí)行不同的代碼呢?這就需要我們進(jìn)行正確的環(huán)境配置和管理润梯。

## 介紹

### 1\. 配置文件

正確的配置環(huán)境首先需要我們認(rèn)識(shí)不同環(huán)境配置之間的關(guān)系过牙,如圖所示:

![](https://user-gold-cdn.xitu.io/2018/11/25/16749778e85b5370?w=356&h=318&f=png&s=24190)

我們從上圖中可以了解到每一個(gè)環(huán)境其實(shí)有其不同的配置,同時(shí)它們也存在著交集部分纺铭,交集便是它們都共有的配置項(xiàng)寇钉,那么在 Vue 中我們應(yīng)該如何處理呢?

我們可以在根目錄下創(chuàng)建以下形式的文件進(jìn)行不同環(huán)境下變量的配置:

```

.env? ? ? ? ? ? ? ? # 在所有的環(huán)境中被載入

.env.local? ? ? ? ? # 在所有的環(huán)境中被載入舶赔,但會(huì)被 git 忽略

.env.[mode]? ? ? ? # 只在指定的模式中被載入

.env.[mode].local? # 只在指定的模式中被載入扫倡,但會(huì)被 git 忽略

```

比如我們創(chuàng)建一個(gè)名為 .env.stage 的文件,該文件表明其只在 stage 環(huán)境下被加載,在這個(gè)文件中撵溃,我們可以配置如下鍵值對(duì)的變量:

```

NODE_ENV=stage

VUE_APP_TITLE=stage mode

```

這時(shí)候我們?cè)趺丛?vue.config.js 中訪問(wèn)這些變量呢疚鲤?很簡(jiǎn)單,使用 `process.env.[name]` 進(jìn)行訪問(wèn)就可以了缘挑,比如:

```

// vue.config.js

console.log(process.env.NODE_ENV); // development(在終端輸出)

```

當(dāng)你運(yùn)行 `yarn serve` 命令后會(huì)發(fā)現(xiàn)輸出的是 development集歇,因?yàn)?`vue-cli-service serve` 命令默認(rèn)設(shè)置的環(huán)境是 development,你需要修改 package.json 中的 serve 腳本的命令為:

```

"scripts": {

? ? "serve": "vue-cli-service serve --mode stage",

}

```

`--mode stage` 其實(shí)就是修改了 webpack 4 中的 mode 配置項(xiàng)為 stage语淘,同時(shí)其會(huì)讀取對(duì)應(yīng) .env.\[model\] 文件下的配置诲宇,如果沒(méi)找到對(duì)應(yīng)配置文件,其會(huì)使用默認(rèn)環(huán)境 development惶翻,同樣 `vue-cli-service build` 會(huì)使用默認(rèn)環(huán)境 production姑蓝。

這時(shí)候如果你再創(chuàng)建一個(gè) .env 的文件,再次配置重復(fù)的變量吕粗,但是值不同纺荧,如:

```

NODE_ENV=staging

VUE_APP_TITLE=staging mode

VUE_APP_NAME=project

```

因?yàn)?.env 文件會(huì)被所有環(huán)境加載,即公共配置颅筋,那么最終我們運(yùn)行 `vue-cli-service serve` 打印出來(lái)的是哪個(gè)呢宙暇?答案是 **stage**,但是如果是 .env.stage.local 文件中配置成上方這樣议泵,答案便是 **staging**客给,所以 .env.\[mode\].local 會(huì)覆蓋 .env.\[mode\] 下的相同配置。同理 .env.local 會(huì)覆蓋 .env 下的相同配置肢簿。

由此可以得出結(jié)論靶剑,相同配置項(xiàng)的權(quán)重:

```

.env.[mode].local > .env.[mode] > .env.local > .env

```

但是需要注意的是,除了相同配置項(xiàng)權(quán)重大的覆蓋小的池充,不同配置項(xiàng)它們會(huì)進(jìn)行合并操作桩引,類(lèi)似于 Javascript 中的 Object.assign 的用法。

### 2\. 環(huán)境注入

通過(guò)上述配置文件的創(chuàng)建收夸,我們成功使用命令行的形式對(duì)項(xiàng)目環(huán)境進(jìn)行了設(shè)置并可以自由切換坑匠,但是需要注意的是我們?cè)?Vue 的前端代碼中打印出的 `process.env` 與 vue.config.js 中輸出的可能是不一樣的,這需要普及一個(gè)知識(shí)點(diǎn):webpack 通過(guò) DefinePlugin 內(nèi)置插件將 process.env 注入到客戶端代碼中卧惜。

```

// webpack 配置

{

? ? ...


? ? plugins: [

? ? ? ? new webpack.DefinePlugin({

? ? ? ? ? ? 'process.env': {

? ? ? ? ? ? ? ? NODE_ENV: JSON.stringify(process.env.NODE_ENV)

? ? ? ? ? ? }

? ? ? ? }),

? ? ],


? ? ...

}

```

由于 vue-cli 3.x 封裝的 webpack 配置中已經(jīng)幫我們完成了這個(gè)功能厘灼,所以我們可以直接在客戶端代碼中打印出 process.env 的值,該對(duì)象可以包含多個(gè)鍵值對(duì)咽瓷,也就是說(shuō)可以注入多個(gè)值设凹,但是經(jīng)過(guò) CLI 封裝后僅支持注入環(huán)境配置文件中以 `VUE_APP_` 開(kāi)頭的變量,而 `NODE_ENV` 和 `BASE_URL` 這兩個(gè)特殊變量除外茅姜。比如我們?cè)跈?quán)重最高的 .env.stage.local 文件中寫(xiě)入:

```

NODE_ENV=stage2

VUE_APP_TITLE=stage mode2

NAME=vue

```

然后我們嘗試在 vue.config.js 中打印 `process.env`闪朱,終端輸出:

```

{

? ? ...


? ? npm_config_ignore_scripts: '',

? ? npm_config_version_git_sign: '',

? ? npm_config_ignore_optional: '',

? ? npm_config_init_version: '1.0.0',

? ? npm_package_dependencies_vue_router: '^3.0.1',

? ? npm_config_version_tag_prefix: 'v',

? ? npm_node_execpath: '/usr/local/bin/node',

? ? NODE_ENV: 'stage2',

? ? VUE_APP_TITLE: 'stage mode2',

? ? NAME: 'vue',

? ? BABEL_ENV: 'development',


? ? ...

}

```

可以看到輸出內(nèi)容除了我們環(huán)境配置中的變量外還包含了很多 npm 的信息,但是我們?cè)谌肟谖募?main.js 中打印會(huì)發(fā)現(xiàn)輸出:

```

{

? ? "BASE_URL": "/vue/",

? ? "NODE_ENV": "stage2",

? ? "VUE_APP_TITLE": "stage mode2"

}

```

可見(jiàn)注入時(shí)過(guò)濾調(diào)了非 `VUE_APP_` 開(kāi)頭的變量,其中多出的 `BASE_URL` 為你在 vue.config.js 設(shè)置的值奋姿,默認(rèn)為 /锄开,其在環(huán)境配置文件中設(shè)置無(wú)效。

![](https://user-gold-cdn.xitu.io/2018/11/25/167497acd942516e?w=544&h=308&f=png&s=32493)

### 3\. 額外配置

以上我們通過(guò)新建配置文件的方式為項(xiàng)目不同環(huán)境配置不同的變量值称诗,能夠?qū)崿F(xiàn)項(xiàng)目基本的環(huán)境管理萍悴,但是 .env 這樣的配置文件中的參數(shù)目前只支持靜態(tài)值,無(wú)法使用動(dòng)態(tài)參數(shù)寓免,在某些情況下無(wú)法實(shí)現(xiàn)特定需求退腥,這時(shí)候我們可以在根目錄下新建 config 文件夾用于存放一些額外的配置文件。

```

/* 配置文件 index.js */

// 公共變量

const com = {

? ? IP: JSON.stringify('xxx')

};

module.exports = {

? ? // 開(kāi)發(fā)環(huán)境變量

? ? dev: {

? ? env: {

? ? ? ? ? ? TYPE: JSON.stringify('dev'),

? ? ? ? ? ? ...com

? ? }

? ? },


? ? // 生產(chǎn)環(huán)境變量

? ? build: {

? ? env: {

? ? ? ? ? ? TYPE: JSON.stringify('prod'),

? ? ? ? ? ? ...com

? ? }

? ? }

}

```

上方代碼我們把環(huán)境變量分為了公共變量再榄、開(kāi)發(fā)環(huán)境變量和生產(chǎn)環(huán)境變量,當(dāng)然這些變量可能是動(dòng)態(tài)的享潜,比如用戶的 ip 等±福現(xiàn)在我們要在 vue.config.js 里注入這些變量,我們可以使用 chainWebpack 修改 DefinePlugin 中的值:

```

/* vue.config.js */

const configs = require('./config');

// 用于做相應(yīng)的 merge 處理

const merge = require('webpack-merge');

// 根據(jù)環(huán)境判斷使用哪份配置

const cfg = process.env.NODE_ENV === 'production' ? configs.build.env : configs.dev.env;

module.exports = {

? ? ...


? ? chainWebpack: config => {

? ? ? ? config.plugin('define')

? ? ? ? ? ? .tap(args => {

? ? ? ? ? ? ? ? let name = 'process.env';


? ? ? ? ? ? ? ? // 使用 merge 保證原始值不變

? ? ? ? ? ? ? ? args[0][name] = merge(args[0][name], cfg);


? ? ? ? ? ? ? ? return args

? ? ? ? ? ? })

? ? },

? ? ...

}

```

最后我們可以在客戶端成功打印出包含動(dòng)態(tài)配置的對(duì)象:

```

{

? ? "NODE_ENV": "stage2",

? ? "VUE_APP_TITLE": "stage mode2",

? ? "BASE_URL": "/vue/",

? ? "TYPE": "dev",

? ? "IP": "xxx"

}

```

### 4\. 實(shí)際場(chǎng)景

結(jié)合以上環(huán)境變量的配置剑按,我們項(xiàng)目中一般會(huì)遇到一些實(shí)際場(chǎng)景: 比如在非線上環(huán)境我們可以給自己的移動(dòng)端項(xiàng)目開(kāi)啟 [vConsole](https://github.com/Tencent/vConsole) 調(diào)試疾就,但是在線上環(huán)境肯定不需要開(kāi)啟這一功能,我們可以在入口文件中進(jìn)行設(shè)置艺蝴,代碼如下:

```

/* main.js */

import Vue from 'vue'

import App from './App.vue'

import router from './router'

import store from './store'

Vue.config.productionTip = false

// 如果是非線上環(huán)境猬腰,加載 VConsole

if (process.env.NODE_ENV !== 'production') {

? ? var VConsole = require('vconsole/dist/vconsole.min.js');

? ? var vConsole = new VConsole();

}

new Vue({

? router,

? store,

? render: h => h(App)

}).$mount('#app')

```

vConsole 是一款用于移動(dòng)網(wǎng)頁(yè)的輕量級(jí),可擴(kuò)展的前端開(kāi)發(fā)工具猜敢,可以看作是移動(dòng)端瀏覽器的控制臺(tái),如圖:

![](https://user-gold-cdn.xitu.io/2018/7/25/164d2204ddfd1384?w=320&h=568&f=png&s=57903)

另外我們還可以使用配置中的 BASE\_URL 來(lái)設(shè)置路由的 base 參數(shù):

```

/* router.js */

import Vue from 'vue'

import Router from 'vue-router'

import Home from './views/Home.vue'

import About from './views/About.vue'

Vue.use(Router)

let base = `${process.env.BASE_URL}`; // 獲取二級(jí)目錄

export default new Router({

? ? mode: 'history',

? ? base: base, // 設(shè)置 base 值

? ? routes: [

? ? ? ? {

? ? ? ? ? ? path: '/',

? ? ? ? ? ? name: 'home',

? ? ? ? ? ? component: Home

? ? ? ? },

? ? ? ? {

? ? ? ? ? ? path: '/about',

? ? ? ? ? ? name: 'about',

? ? ? ? ? ? component: About

? ? ? ? }

? ? ]

})

```

每一個(gè)環(huán)境變量你都可以用于項(xiàng)目的一些地方,它提供給了我們一種全局的可訪問(wèn)形式踱稍,也是基于 Node 開(kāi)發(fā)的特性所在廊蜒。

## 結(jié)語(yǔ)

環(huán)境的配置和管理對(duì)于項(xiàng)目的構(gòu)建起到了至關(guān)重要的作用,通過(guò)給項(xiàng)目配置不同的環(huán)境不僅可以增加開(kāi)發(fā)的靈活性胯盯、提高程序的拓展性懈费,同時(shí)也有助于幫助我們?nèi)チ私獠⒎治鲰?xiàng)目在不同環(huán)境下的運(yùn)行機(jī)制,建立全局觀念博脑。

## 思考 & 作業(yè)

*? webpack 通過(guò) DefinePlugin 內(nèi)置插件將 process.env 注入到客戶端代碼中時(shí)憎乙,`process.env.NODE_ENV` 為什么要進(jìn)行 JSON.stringify 處理?


*? `process.env` 中如何獲取 package.json 中 name 的值叉趣?


*? 如何在 package.json 中的 scripts 字段中定義一些自定義腳本來(lái)切換不同的環(huán)境泞边?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市疗杉,隨后出現(xiàn)的幾起案子繁堡,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件椭蹄,死亡現(xiàn)場(chǎng)離奇詭異闻牡,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)绳矩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)罩润,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人翼馆,你說(shuō)我怎么就攤上這事割以。” “怎么了应媚?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵严沥,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我中姜,道長(zhǎng)消玄,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任丢胚,我火速辦了婚禮翩瓜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘携龟。我一直安慰自己兔跌,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布峡蟋。 她就那樣靜靜地躺著坟桅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蕊蝗。 梳的紋絲不亂的頭發(fā)上桦卒,一...
    開(kāi)封第一講書(shū)人閱讀 51,562評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音匿又,去河邊找鬼方灾。 笑死,一個(gè)胖子當(dāng)著我的面吹牛碌更,可吹牛的內(nèi)容都是我干的裕偿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼痛单,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼嘿棘!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起旭绒,我...
    開(kāi)封第一講書(shū)人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤鸟妙,失蹤者是張志新(化名)和其女友劉穎焦人,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體重父,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡花椭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了房午。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片矿辽。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖郭厌,靈堂內(nèi)的尸體忽然破棺而出袋倔,到底是詐尸還是另有隱情,我是刑警寧澤折柠,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布宾娜,位于F島的核電站,受9級(jí)特大地震影響扇售,放射性物質(zhì)發(fā)生泄漏前塔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一缘眶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧髓废,春花似錦巷懈、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至冈爹,卻和暖如春涌攻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背频伤。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工恳谎, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人憋肖。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓因痛,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親岸更。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鸵膏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355