webpack多頁應用架構系列(七):開發環境、生產環境傻傻分不清楚?

本文首發於 Array_Huang的技術博客—— 實用至上,非經做者贊成,請勿轉載。
原文地址: http://www.javashuo.com/article/p-gfjsypje-kc.html
若是您對本系列文章感興趣,歡迎關注訂閱這裏: https://segmentfault.com/blog/array_huang

前言

開發環境與生產環境分離的緣由以下:javascript

  • 在開發時,不可避免會產生大量debug又或是測試的代碼,這些代碼不該出如今生產環境中(也即不該提供給用戶)。
  • 在把頁面部署到服務器時,爲了追求極致的技術指標,咱們會對代碼進行各類各樣的優化,好比說混淆、壓縮,這些手段每每會完全破壞代碼自己的可讀性,不利於咱們進行debug等工做。
  • 數據源的差別化,好比說在本地開發時,讀取的每每是本地mock出來的數據,而正式上線後讀取的天然是API提供的數據了。

若是硬是要在開發環境和生產環境用徹底同樣的代碼,那麼必然會付出沉重的代價,這點想必也不用多說了。css

下面主要針對兩點來介紹如何分離開發環境和生產環境:一是如何以不一樣的方式進行編譯,也即如何分別造成開發環境及生產環境的webpack配置文件;二是在業務代碼中如何根據環境的不一樣而作出不一樣的處理。html

如何分離開發環境和生產環境的webpack配置文件

若是同時把一份完整的開發環境配置文件和一份完整的生產環境配置文件列在一塊兒進行比較,那麼會出現如下三種狀況:前端

  • 開發環境有的配置,生產環境不必定有,好比說開發時須要生成sourcemap來幫助debug,又或是熱更新時使用到的HotModuleReplacementPlugin
  • 生產環境有的配置,開發環境不必定有,好比說用來混淆壓縮js用的UglifyJsPlugin
  • 開發環境和生產環境都擁有的配置,但在細節上有所不一樣,好比說output.publicPath,又好比說css-loader中的minimizeautoprefixer參數。

更重要的是,實際上開發環境和生產環境的配置文件的絕大部分都是一致的,對於這一致的部分來講,咱們堅定要消除冗餘,不然後續維護起來不只麻煩,並且還容易出錯。java

怎麼作呢?

答案很簡單:分拆webpack配置文件成N個小module。原先咱們是一個完整的配置文件,有好幾百行,從頭看到尾都頭大了,更別說分離不分離的了。下面來看看我分離的結果:node

├─webpack.dev.config.js # 開發環境的webpack配置文件(無實質內容,僅爲組織整理)
├─webpack.config.js # 生產環境的webpack配置文件(無實質內容,僅爲組織整理)
├─webpack-config # 存放分拆後的webpack配置文件
  ├─entry.config.js # webpack配置中的各個大項,這一級目錄裏的文件都是
  ├─module.config.js
  ├─output.config.js
  ├─plugins.dev.config.js # 倆環境配置中不一致的部分,此文件由開發環境配置文件webpack.dev.config.js來加載
  ├─plugins.product.config.js # 倆環境配置中不一致的部分,此文件由生產環境配置文件webpack.config.js來加載
  ├─resolve.config.js
  │  
  ├─base # 主要是存放一些變量
  │   ├─dir-vars.config.js
  │   ├─page-entries.config.js
  │      
  ├─inherit # 存放生產環境和開發環境相同的部分,以供繼承
  │   ├─plugins.config.js
  │      
  └─vendor # 存放webpack兼容第三方庫所需的配置文件
      ├─eslint.config.js
      ├─postcss.config.js

文件目錄結構看過了,接下來看一下我是如何組織整理最後的配置文件的:webpack

/* 開發環境webpack配置文件webpack.dev.config.js */
module.exports = {
  entry: require('./webpack-config/entry.config.js'),

  output: require('./webpack-config/output.config.js'),

  module: require('./webpack-config/module.config.js'),

  resolve: require('./webpack-config/resolve.config.js'),

  plugins: require('./webpack-config/plugins.dev.config.js'),

  eslint: require('./webpack-config/vendor/eslint.config.js'),

  postcss: require('./webpack-config/vendor/postcss.config.js'),
};

這樣,你就能夠很輕鬆地處理開發/生產環境配置文件中相同與不一樣的部分了。git

如何分別調用開發/生產環境的配置文件呢?

還記得我在《webpack多頁應用架構系列(二):webpack配置經常使用部分有哪些?》裏講過,咱們在控制檯調用webpack命令來啓動打包時,能夠添加上--config參數來指定webpack配置文件的路徑嗎?咱們能夠配合上npm scripts來使用,在package.json裏定義:github

"scripts": {
    "build": "node build-script.js && webpack --progress --colors",
    "dev": "node build-script.js && webpack --progress --colors --config ./webpack.dev.config.js",
    "watch": "webpack --progress --colors --watch --config ./webpack.dev.config.js"
  },

這樣一來,當咱們開發的時候就可使用npm run devnpm run watch,而到要上線打包的時候就運行npm run buildweb

業務代碼如何判斷生產/開發環境

在業務代碼裏要判斷生產/開發環境其實很簡單,只需一個變量便可:

if (IS_PRODUCTION) {
    // 作生產環境該作的事情
} else {
    // 作開發環境該作的事情
}

這麼一來,關鍵就在於這變量IS_PRODUCTION是怎麼來的了。

在我還沒分離開發和生產環境時,我用的辦法是,開發時在業務代碼所使用的配置文件中把這變量設爲false,而在最後打包上線時就手動改成true。這種方法我用過一段時間,很是繁瑣,並且常常上線後發現,我嘞個去怎麼ajax讀的是我本地的mock服務器。

怎麼作呢?

我參考了許多文章,先粗略講講我沒有采用的方法:

  • EnvironmentPlugin引入process.env,這樣就能夠在業務代碼中靠process.env.NODE_ENV來判斷了。
  • ProvidePlugin來控制在不一樣環境里加載不一樣的配置文件(業務代碼用的)。

那我用的是什麼方法呢?我最後選用的是DefinePlugin

舉個官方例子,其大概用法是這樣的:

new webpack.DefinePlugin({
    PRODUCTION: JSON.stringify(true),
    VERSION: JSON.stringify("5fa3b9"),
    BROWSER_SUPPORTS_HTML5: true,
    TWO: "1+1",
    "typeof window": JSON.stringify("object")
})

DefinePlugin可能會被誤認爲其做用是在webpack配置文件中爲編譯後的代碼上下文環境設置全局變量,但其實否則。它真正的機制是:DefinePlugin的參數是一個object,那麼其中會有一些key-value對。在webpack編譯的時候,會把業務代碼中沒有定義(使用var/const/let來預約義的)而變量名又與key相同的變量(直接讀代碼的話的確像是全局變量)替換成value。例如上面的官方例子,PRODUCTION就會被替換爲trueVERSION就會被替換爲'5fa3b9'(注意單引號);BROWSER_SUPPORTS_HTML5也是會被替換爲trueTWO會被替換爲1+1(至關因而一個數學表達式);typeof window就被替換爲'object'了。

再舉個例子,好比你在代碼裏是這麼寫的:

if (!PRODUCTION)
    console.log('Debug info')
if (PRODUCTION)
    console.log('Production log')

那麼在編譯生成的代碼裏就會是這樣了:

if (!true)
    console.log('Debug info')
if (true)
    console.log('Production log')

而若是你用了UglifyJsPlugin,則會變成這樣:

console.log('Production log')

如此一來,只要在倆環境的配置文件裏用DefinePlugin分別定義好IS_PRODUCTION的值,咱們就能夠在業務代碼裏進行判斷了:

/* global IS_PRODUCTION:true */
  if (!IS_PRODUCTION) {
    console.log('若是你看到這個Log,那麼這個版本其實是開發用的版本');
  }

須要注意的是,若是你在webpack裏整合了ESLint,那麼,因爲ESLint會檢測沒有定義的變量(ESLint要求使用全局變量時要用window.xxxxx的寫法),所以須要一個global註釋聲明(/* global IS_PRODUCTION:true */)IS_PRODUCTION是一個全局變量(固然在本例中並非)來規避warning。

示例代碼

諸位看本系列文章,搭配我在Github上的腳手架項目食用更佳哦(笑):Array-Huang/webpack-seed(https://github.com/Array-Huang/webpack-seed)

附系列文章目錄(同步更新)

本文首發於 Array_Huang的技術博客—— 實用至上,非經做者贊成,請勿轉載。
原文地址: http://www.javashuo.com/article/p-gfjsypje-kc.html
若是您對本系列文章感興趣,歡迎關注訂閱這裏: https://segmentfault.com/blog/array_huang
相關文章
相關標籤/搜索