為什么需要區(qū)分環(huán)境
在開發(fā)網(wǎng)頁的時候,一般都會有多套運行環(huán)境苫耸,例如:
- 在開發(fā)過程中方便開發(fā)調(diào)試的環(huán)境敢会。
- 發(fā)布到線上給用戶使用的運行環(huán)境。
這兩套不同的環(huán)境雖然都是由同一套源代碼編譯而來忠寻,但是代碼內(nèi)容卻不一樣,差異包括:
- 線上代碼被特殊壓縮過存和。
- 開發(fā)用的代碼包含一些用于提示開發(fā)者的提示日志奕剃,這些日志普通用戶不可能去看它。
- 開發(fā)用的代碼所連接的后端數(shù)據(jù)接口地址也可能和線上環(huán)境不同捐腿,因為要避免開發(fā)過程中造成對線上數(shù)據(jù)的影響纵朋。
為了盡可能的復(fù)用代碼,在構(gòu)建的過程中需要根據(jù)目標(biāo)代碼要運行的環(huán)境而輸出不同的代碼茄袖,我們需要一套機制在源碼中去區(qū)分環(huán)境倡蝙。 幸運的是Webpack已經(jīng)為我們實現(xiàn)了這點。
如何區(qū)分環(huán)境
具體區(qū)分方法很簡單绞佩,在源碼中通過如下方式:
if (process.env.NODE_ENV === 'production') {
console.log('你正在線上環(huán)境');
} else {
console.log('你正在使用開發(fā)環(huán)境');
}
其大概原理是借助于環(huán)境變量的值去判斷執(zhí)行哪個分支。
當(dāng)你的代碼中出現(xiàn)了使用process
模塊的語句時猪钮,Webpack就自動打包進process
模塊的代碼以支持非Node.js的運行環(huán)境品山。 當(dāng)你的代碼中沒有使用process
時就不會打包進process
模塊的代碼。這個注入的process
模塊作用是為了模擬Node.js中的process
烤低,以支持上面使用的process.env.NODE_ENV === 'production'
語句肘交。
在構(gòu)建線上環(huán)境代碼時,需要給當(dāng)前運行環(huán)境設(shè)置環(huán)境變量NODE_ENV = 'production'
扑馁,Webpack相關(guān)配置如下:
const DefinePlugin = require('webpack/lib/DefinePlugin');
module.exports = {
plugins: [
new DefinePlugin({
// 定義 NODE_ENV 環(huán)境變量為 production
'process.env': {
NODE_ENV: JSON.stringify('production')
}
}),
],
};
注意在定義環(huán)境變量的值時用JSON.stringify
包裹字符串的原因是環(huán)境變量的值需要是一個由雙引號包裹的字符串涯呻,而JSON.stringify('production')
的值正好等于'"production"'
。
執(zhí)行構(gòu)建后腻要,你會在輸出的文件中發(fā)現(xiàn)如下代碼:
if (true) {
console.log('你正在使用線上環(huán)境');
} else {
console.log('你正在使用開發(fā)環(huán)境');
}
定義的環(huán)境變量的值被代入到了源碼中复罐,process.env.NODE_ENV === 'production'
被直接替換成了true
。并且由于此時訪問process
的語句被替換了而沒有了雄家,Webpack也不會打包進process
模塊了效诅。
DefinePlugin定義的環(huán)境變量只對Webpack需要處理的代碼有效,而不會影響Node.js運行時的環(huán)境變量的值。
通過Shell腳本的方式去定義的環(huán)境變量乱投,例如NODE_ENV=production webpack
咽笼,Webpack是不認識的,對Webpack需要處理的代碼中的環(huán)境區(qū)分語句是沒有作用的戚炫。
也就是說只需要通過DefinePlugin定義環(huán)境變量就能使上面介紹的環(huán)境區(qū)分語句正常工作剑刑,沒必要又通過Shell腳本的方式去定義一遍。
如果想讓W(xué)ebpack使用通過Shell腳本的方式去定義的環(huán)境變量双肤,可以使用EnvironmentPlugin施掏,代碼如下:
new webpack.EnvironmentPlugin(['NODE_ENV'])
以上這句代碼實際上等價于:
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
})
結(jié)合UglifyJS
其實以上輸出的代碼還可以進一步優(yōu)化,因為if(true)
語句永遠只會執(zhí)行前一個分支中的代碼杨伙,也就是說最佳的輸出其實應(yīng)該直接是:
console.log('你正在線上環(huán)境');
Webpack沒有實現(xiàn)去除死代碼功能其监,但是UglifyJS可以做這個事情。
第三方庫中的環(huán)境區(qū)分
除了在自己寫的源碼中可以有環(huán)境區(qū)分的代碼外限匣,很多第三方庫也做了環(huán)境區(qū)分的優(yōu)化抖苦。 以React為例,它做了兩套環(huán)境區(qū)分米死,分別是:
- 開發(fā)環(huán)境:包含類型檢查锌历、HTML元素檢查等等針對開發(fā)者的警告日志代碼。
- 線上環(huán)境:去掉了所有針對開發(fā)者的代碼峦筒,只保留讓React能正常運行的部分究西,以優(yōu)化大小和性能。
例如React源碼中有大量類似下面這樣的代碼:
if (process.env.NODE_ENV !== 'production') {
warning(false, '%s(...): Can only update a mounted or mounting component.... ')
}
如果你不定義NODE_ENV=production
那么這些警告日志就會被包含到輸出的代碼中物喷,輸出的文件將會非常大卤材。
process.env.NODE_ENV !== 'production'
中的NODE_ENV
和'production'
兩個值是社區(qū)的約定,通常使用這條判斷語句區(qū)分開發(fā)環(huán)境和線上環(huán)境峦失。