鄙人已付費購買,粘貼出來給更多前端愛好者學(xué)習(xí)招刨。若掘金小冊禁止此類操作哀军,請聯(lián)系
在實際項目的開發(fā)中,我們一般會經(jīng)歷項目的開發(fā)階段谎倔、測試階段和最終上線階段猿推,每一個階段對于項目代碼的要求可能都不盡相同,那么我們?nèi)绾文軌蛴稳杏杏嗟脑诓煌A段下使我們的項目呈現(xiàn)不同的效果蹬叭,使用不同的功能呢?這里就需要引入環(huán)境的概念孽查。
一般一個項目都會有以下 3 種環(huán)境:
開發(fā)環(huán)境(開發(fā)階段坦喘,本地開發(fā)版本,一般會使用一些調(diào)試工具或額外的輔助功能)
測試環(huán)境(測試階段洲胖,上線前版本坯沪,除了一些 bug 的修復(fù)擒滑,基本不會和上線版本有很大差別)
生產(chǎn)環(huán)境(上線階段,正式對外發(fā)布的版本藻糖,一般會進(jìn)行優(yōu)化库车,關(guān)掉錯誤報告)
作為一名開發(fā)人員,我們可能需要針對每一種環(huán)境編寫一些不同的代碼并且保證這些代碼運行在正確的環(huán)境中洋满,那么我們應(yīng)該如何在代碼中判斷項目所處的環(huán)境同時執(zhí)行不同的代碼呢?這就需要我們進(jìn)行正確的環(huán)境配置和管理正罢。
正確的配置環(huán)境首先需要我們認(rèn)識不同環(huán)境配置之間的關(guān)系驻民,如圖所示:
我們從上圖中可以了解到每一個環(huán)境其實有其不同的配置,同時它們也存在著交集部分回还,交集便是它們都共有的配置項柠硕,那么在 Vue 中我們應(yīng)該如何處理呢?
我們可以在根目錄下創(chuàng)建以下形式的文件進(jìn)行不同環(huán)境下變量的配置:
.env# 在所有的環(huán)境中被載入
.env.local# 在所有的環(huán)境中被載入仅叫,但會被git忽略
.env.[mode]# 只在指定的模式中被載入
.env.[mode].local# 只在指定的模式中被載入诫咱,但會被git忽略
比如我們創(chuàng)建一個名為 .env.stage 的文件,該文件表明其只在 stage 環(huán)境下被加載坎缭,在這個文件中掏呼,我們可以配置如下鍵值對的變量:
NODE_ENV=stage
VUE_APP_TITLE=stage mode
這時候我們怎么在 vue.config.js 中訪問這些變量呢?很簡單憎夷,使用process.env.[name]進(jìn)行訪問就可以了,比如:
// vue.config.js
console.log(process.env.NODE_ENV);// development(在終端輸出)
當(dāng)你運行yarn serve命令后會發(fā)現(xiàn)輸出的是 development祥得,因為vue-cli-service serve命令默認(rèn)設(shè)置的環(huán)境是 development蒋得,你需要修改 package.json 中的 serve 腳本的命令為:
"scripts": {
"serve":"vue-cli-service serve --mode stage",
}
--mode stage其實就是修改了 webpack 4 中的 mode 配置項為 stage,同時其會讀取對應(yīng) .env.[model] 文件下的配置饮焦,如果沒找到對應(yīng)配置文件,其會使用默認(rèn)環(huán)境 development械哟,同樣vue-cli-service build會使用默認(rèn)環(huán)境 production殿雪。
這時候如果你再創(chuàng)建一個 .env 的文件,再次配置重復(fù)的變量爸业,但是值不同亏镰,如:
NODE_ENV=staging
VUE_APP_TITLE=staging mode
VUE_APP_NAME=project
因為 .env 文件會被所有環(huán)境加載,即公共配置索抓,那么最終我們運行vue-cli-service serve打印出來的是哪個呢逼肯?答案是stage,但是如果是 .env.stage.local 文件中配置成上方這樣篮幢,答案便是staging三椿,所以 .env.[mode].local 會覆蓋 .env.[mode] 下的相同配置。同理 .env.local 會覆蓋 .env 下的相同配置搜锰。
由此可以得出結(jié)論,相同配置項的權(quán)重:
.env.[mode].local>.env.[mode]>.env.local>.env
但是需要注意的是焊傅,除了相同配置項權(quán)重大的覆蓋小的鸦列,不同配置項它們會進(jìn)行合并操作,類似于 Javascript 中的 Object.assign 的用法。
通過上述配置文件的創(chuàng)建骆姐,我們成功使用命令行的形式對項目環(huán)境進(jìn)行了設(shè)置并可以自由切換,但是需要注意的是我們在 Vue 的前端代碼中打印出的process.env與 vue.config.js 中輸出的可能是不一樣的玻褪,這需要普及一個知識點:webpack 通過 DefinePlugin 內(nèi)置插件將 process.env 注入到客戶端代碼中带射。
// webpack 配置
{
? ? ...
? ? plugins: [
newwebpack.DefinePlugin({
'process.env': {
NODE_ENV:JSON.stringify(process.env.NODE_ENV)
? ? ? ? ? ? }
? ? ? ? }),
? ? ],
? ? ...
}
由于 vue-cli 3.x 封裝的 webpack 配置中已經(jīng)幫我們完成了這個功能,所以我們可以直接在客戶端代碼中打印出 process.env 的值窟社,該對象可以包含多個鍵值對灿里,也就是說可以注入多個值,但是經(jīng)過 CLI 封裝后僅支持注入環(huán)境配置文件中以VUE_APP_開頭的變量匣吊,而NODE_ENV和BASE_URL這兩個特殊變量除外色鸳。比如我們在權(quán)重最高的 .env.stage.local 文件中寫入:
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 的信息褥影,但是我們在入口文件 main.js 中打印會發(fā)現(xiàn)輸出:
{
"BASE_URL":"/vue/",
"NODE_ENV":"stage2",
"VUE_APP_TITLE":"stage mode2"
}
可見注入時過濾調(diào)了非VUE_APP_開頭的變量咏雌,其中多出的BASE_URL為你在 vue.config.js 設(shè)置的值,默認(rèn)為 /统倒,其在環(huán)境配置文件中設(shè)置無效氛雪。
以上我們通過新建配置文件的方式為項目不同環(huán)境配置不同的變量值,能夠?qū)崿F(xiàn)項目基本的環(huán)境管理浴鸿,但是 .env 這樣的配置文件中的參數(shù)目前只支持靜態(tài)值弦追,無法使用動態(tài)參數(shù),在某些情況下無法實現(xiàn)特定需求掸哑,這時候我們可以在根目錄下新建 config 文件夾用于存放一些額外的配置文件。
/* 配置文件 index.js */
// 公共變量
constcom = {
IP:JSON.stringify('xxx')
};
module.exports = {
// 開發(fā)環(huán)境變量
? ? dev: {
env: {
TYPE:JSON.stringify('dev'),
? ? ? ? ? ? ...com
? ? }
? ? },
// 生產(chǎn)環(huán)境變量
? ? build: {
env: {
TYPE:JSON.stringify('prod'),
? ? ? ? ? ? ...com
? ? }
? ? }
}
上方代碼我們把環(huán)境變量分為了公共變量厌蔽、開發(fā)環(huán)境變量和生產(chǎn)環(huán)境變量摔癣,當(dāng)然這些變量可能是動態(tài)的,比如用戶的 ip 等≡褡牵現(xiàn)在我們要在 vue.config.js 里注入這些變量近她,我們可以使用 chainWebpack 修改 DefinePlugin 中的值:
/* vue.config.js */
constconfigs =require('./config');
// 用于做相應(yīng)的 merge 處理
constmerge =require('webpack-merge');
// 根據(jù)環(huán)境判斷使用哪份配置
constcfg = process.env.NODE_ENV ==='production'? configs.build.env : configs.dev.env;
module.exports = {
? ? ...
chainWebpack:config=>{
config.plugin('define')
.tap(args=>{
letname ='process.env';
// 使用 merge 保證原始值不變
args[0][name] = merge(args[0][name], cfg);
returnargs
? ? ? ? ? ? })
? ? },
? ? ...
}
最后我們可以在客戶端成功打印出包含動態(tài)配置的對象:
{
"NODE_ENV":"stage2",
"VUE_APP_TITLE":"stage mode2",
"BASE_URL":"/vue/",
"TYPE":"dev",
"IP":"xxx"
}
結(jié)合以上環(huán)境變量的配置,我們項目中一般會遇到一些實際場景: 比如在非線上環(huán)境我們可以給自己的移動端項目開啟vConsole調(diào)試薇缅,但是在線上環(huán)境肯定不需要開啟這一功能攒磨,我們可以在入口文件中進(jìn)行設(shè)置,代碼如下:
/* main.js */
importVuefrom'vue'
importAppfrom'./App.vue'
importrouterfrom'./router'
importstorefrom'./store'
Vue.config.productionTip =false
// 如果是非線上環(huán)境灸撰,加載 VConsole
if(process.env.NODE_ENV !=='production') {
varVConsole =require('vconsole/dist/vconsole.min.js');
varvConsole =newVConsole();
}
newVue({
? router,
? store,
render:h=>h(App)
}).$mount('#app')
vConsole 是一款用于移動網(wǎng)頁的輕量級拼坎,可擴(kuò)展的前端開發(fā)工具,可以看作是移動端瀏覽器的控制臺债蓝,如圖:
另外我們還可以使用配置中的 BASE_URL 來設(shè)置路由的 base 參數(shù):
/* router.js */
importVuefrom'vue'
importRouterfrom'vue-router'
importHomefrom'./views/Home.vue'
importAboutfrom'./views/About.vue'
Vue.use(Router)
letbase =`${process.env.BASE_URL}`;// 獲取二級目錄
exportdefaultnewRouter({
mode:'history',
base: base,// 設(shè)置 base 值
? ? routes: [
? ? ? ? {
path:'/',
name:'home',
component: Home
? ? ? ? },
? ? ? ? {
path:'/about',
name:'about',
component: About
? ? ? ? }
? ? ]
})
每一個環(huán)境變量你都可以用于項目的一些地方饰迹,它提供給了我們一種全局的可訪問形式余舶,也是基于 Node 開發(fā)的特性所在。
環(huán)境的配置和管理對于項目的構(gòu)建起到了至關(guān)重要的作用匿值,通過給項目配置不同的環(huán)境不僅可以增加開發(fā)的靈活性千扔、提高程序的拓展性库正,同時也有助于幫助我們?nèi)チ私獠⒎治鲰椖吭诓煌h(huán)境下的運行機(jī)制厘唾,建立全局觀念龙誊。
webpack 通過 DefinePlugin 內(nèi)置插件將 process.env 注入到客戶端代碼中時,process.env.NODE_ENV為什么要進(jìn)行 JSON.stringify 處理鹤树?
process.env中如何獲取 package.json 中 name 的值逊朽?
如何在 package.json 中的 scripts 字段中定義一些自定義腳本來切換不同的環(huán)境?