webpack4生產環境和開發環境的對比

霽茶茶發表於2019-04-08

前言

近期一直在看webpack4的文件,於是給自己做了這個總結,對比一下生產環境和開發環境的區別。

開發環境

在專案開發過程中,我們關注的是能否追溯到程式碼的錯誤來源,能夠及時重新整理頁面讓我們看到程式碼的實際效果,因此webpack針對開發特點提供了幾個外掛。

source-map

webpack會將程式碼打包至一個檔案中,一旦發生錯誤和警告,很難追溯到其來源,因此webpack提供了source-map,將編譯後的程式碼對映回原始程式碼。

官網示例程式碼結果如圖:

webpack4生產環境和開發環境的對比

source-map有多種使用方式,官方推薦三種使用方式:devtool, SourceMapDevToolPlugin或EvalSourceMapDevToolPlugin。第一種採用的是內建外掛,第二、第三種是直接插入外掛。三種方式不能同時出現,否則會導致外掛被置入兩次。

devtool

devtool: 'inline-source-map',
複製程式碼

webpack官網中列舉出來了一些devtool屬性的效能對比(地址),我們可以根據自己專案的需要來選用。

外掛可以對source-map生成更細粒度的控制,可以作為外掛使用,也可以在通過devtool的某些設定來自動開啟

SourceMapDevToolPlugin

new webpack.SourceMapDevToolPlugin(options);
複製程式碼

在使用TerserPlugin時,您必須使用該sourceMap選項。

EvalSourceMapDevToolPlugin

new webpack.EvalSourceMapDevToolPlugin(options);
複製程式碼

開發工具

每次編寫完程式碼後都需要執行npm run build非常麻煩,因此webpack提供了三種不同的開發工具支援實時更新程式碼。

1. webpack's Watch Mode

通過新增npm指令碼的方式來監控程式碼的變化,在package.json中新增一行

"scripts": {
	"watch": "webpack --watch"
}
複製程式碼

此時啟動npm run watch便可以監控程式碼,我們可以檢視到檔案的hash值一直在變化,表示檔案一直在跟隨程式碼的更新而變化。

webpack4生產環境和開發環境的對比
但是這個方式依舊需要開發人員手動重新整理瀏覽器才能看到更改,因此使用者不多。

2. webpack-dev-server

該方法提供一個簡單的web伺服器和使用實時重新載入的能力。因此是使用較多的一個工具。webpack-dev-server從devServer中讀取配置,

devServer: {
    contentBase: './dist'
}
複製程式碼

contentBase告訴webpack-dev-server從dist目錄中提供檔案,顯示在localhost:8080上。

webpack-dev-server並不會輸出編譯後的檔案,而是將檔案儲存在記憶體中提供服務,開啟本地伺服器來監控程式碼並實時重新整理網頁。

webpack-dev-server開啟的是本地伺服器,而通常我們在開發過程中需要與後端進行通訊,除錯資料。本地伺服器會有跨域問題,因此我們可以通過devServer設定代理來請求介面

devServer: {
    proxy: {
        '/api': 'http://localhost:3000'
    }
}
複製程式碼

當然我們也可以通過一些代理軟體來對處理本地請求的問題,只是webpack-dev-server自己也實現了這個功能。

更多具體的配置可以查閱官網文件

3. webpack-dev-middleware

webpack-dev-middleware是一個包裝器,將webpack處理後的檔案傳送到伺服器,在webpack-dev-server內部也是通過這個中介軟體來實現的,也可以作為單獨的包提供,允許更多自定義設定。

webpack-dev-middleware在使用時需要配置output的publicPath屬性,以確保正確提供檔案。新增檔案server.js, 在該檔案中設定相應的自定義,便可以啟動中介軟體執行。下面是官網的自定義示例:

webpack4生產環境和開發環境的對比

生產環境

生產環境與開發環境完全不同,在生產環境中我們關注的是如何才能產生更小的程式碼塊,壓縮檔案的體積,使得載入時間做到最短。

source mapping

webpack鼓勵開發人員在生產環境中也加入source-map,便於除錯和執行基準測試。在生產環境中應當選擇構建速度較快的source-map,儘量避免在生產環境中使用inline-*** 或 eval-*** 的source-map,因為它們會顯著增加包的體積並降低整體效能。

推薦配置:devtool: 'source-map'

壓縮css

優化css程式碼是生產構建中非常重要的一環,webpack4+為此專門提供了一個css外掛:MiniCssExtractPlugin,為每個包含CSS的JS檔案建立一個CSS檔案。它支援CSS和SourceMaps的按需載入。 與之前使用的extract-text-webpack-plugin外掛(webpack4+已廢棄)相比,webpack4提供的這個外掛具有更加顯著的優點:非同步載入、沒有重複編譯、效能更加良好、更容易使用、定製css。而且官網指明在未來這個外掛有可能會支援熱過載。

需要注意的是,MiniCssExtractPlugin只應該在生產環境中被使用,而且在生產環境中應該使用它來替代style-loader。同時配合外掛optimize-css-assets-webpack-plugin來壓縮css檔案。

module.exports = {
    optimization: {
    minimizer: [
      new TerserJSPlugin({}), // 需要顯式的指定js minimalizer,因為optimization.minimizer會覆蓋預設設定
      new OptimizeCSSAssetsPlugin({})
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: "[name].css",
      chunkFilename: "[id].css"
    })
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          "css-loader"
        ]
      }
    ]
  }
}
module: {
    rules: [
        {
            test: /\.css$/,
            use: [
                MiniCssExtractPlugin.loader,
                "css-loader"
            ]
        }
    ]
}
複製程式碼

Tree Shaking

tree shaking是Javascript上下文中去除死程式碼的一個術語,它依賴於ES5的模組語法的靜態結構,即import和export語法。在webpack2中已內建支援ES2015的模組語法,在webpack4中隊這一功能進行了擴充,通過package.json中的'sideEffects'來標識專案中哪些檔案是純粹的,如果未使用則可以安全修建。 但是在使用tree shaking時需要注意,只有無副作用的程式碼才可以應用修剪,

“副作用”定義為在匯入時執行特殊行為的程式碼,而不是公開一個或多個匯出。一個例子是polyfill,它影響全域性範圍,通常不提供匯出。

在webpack4以上的環境中,在package.json中新增'sideEffects'屬性,並將mode設定為production即可開啟tree shaking。當然,tree shaking只是標識出可以被修剪掉的程式碼塊,最後還是需要使用UglifyjsWebpackPlugin外掛來進行js壓縮。

注意:tree shaking只支援ES6模組語法,不能識別CommonJS語法,因為CommonJS是動態匯入,無法被識別

通用功能

Code Splitting

在使用webpack的時候,我們都會學到一個概念,webpack會將所有程式碼都打包到一個檔案中去,造成該檔案的體積十分的龐大,因此webpack提供了程式碼拆分功能,我們可以將一些程式碼提取成不同的bundles,實現按需載入或並行載入。使用得當的話能很大的提升載入效率。通常有三種程式碼拆方式:

  • Entry Points: 通過entry的配置來手動拆分程式碼。
  • Prevent Duplication: 使用外掛SplitChunksPlugin來刪除重複程式碼和拆分程式碼。
  • Dynamic Imports: 通過模組內的行內函數呼叫來拆分程式碼。

webpack4.6.0+版本增加了對預載入和預獲取的支援:

  • prefetch:將來某些導航可能需要資源
  • preload:當前導航期間可能需要資源

在webpack4+中,推薦使用SplitChunksPlugin來分離程式碼,這個外掛允許我們將共同的依賴提取到一個現有的塊或者一個全新的塊中去。相較於之前使用的CommonsChunkPlugin外掛(webpack4+已廢棄),webpack在SplitChunksPlugin中做了更多的優化,在基於以下的條件時新的程式碼塊會被自動建立:

  • 新的程式碼塊被共享或者來自node_modules資料夾
  • 新的程式碼塊大於30kb(在min+giz之前)
  • 按需載入程式碼塊的請求數量應該<=5
  • 頁面初始化時載入程式碼塊的請求數量應該<=3

同時SplitChunksPlugin提供了豐富的api供我們實現個性化設定,我們可以根據自己的專案特性來設定如何分離程式碼塊。詳情查詢

SplitChunksPlugin配置如下:

module.exports = {
        //...
        optimization: {
            splitChunks: {
                chunks: 'async',
                minSize: 30000,
                maxSize: 0,
                minChunks: 1,
                maxAsyncRequests: 5,
                maxInitialRequests: 3,
                automaticNameDelimiter: '~',
                name: true,
                cacheGroups: {
                    vendors: {
                        test: /[\\/]node_modules[\\/]/,
                        priority: -10
                    },
                    default: {
                        minChunks: 2,
                        priority: -20,
                        reuseExistingChunk: true
                    }
                }
            }
        }
};
複製程式碼

該外掛支援我們配置快取組來快取不同的檔案,通過test配置來選擇什麼樣的模組可以進入該快取組。

// 該名為vendors的快取組可以快取所有來自node_modules的檔案
splitChunks: {
    cacheGroups: {
        commons: {
            test: /[\\/]node_modules[\\/]/,
            name: "vendors",
            chunks: "all"
        }
    }
}
// 注意:這可能會導致下載額外的程式碼。
// 實際應用中應該只包含核心功能和框架,避免包過於龐大。
複製程式碼

環境

webpack4中對於區分開發和生產環境的不同提出了兩種解決方案:webpack-merge和env環境變數。

webpack-merge

安裝了webpack-merge以後,我們需要修改原本的webpack.config.js

   - webpack.config.js
   + webpack.common.js
   + webpack.dev.js
   + webpack.prod.js
複製程式碼

webpack.common.js是設定entry, output等通用設定和開發環境、生產環境都能使用的外掛的配置檔案。 webpack.dev.js需要設定mode為development,表示這是開發環境,同樣我們的devtool(使用強效source-map),devServer也是配置在這個檔案,我們可以在這個檔案中做一些針對自己專案開發需求的個性化配置。 webpack.prod.js設定mode為production,表示生產環境,此時webpack4預設啟用外掛TerserPlugin,我們可以將壓縮檔案的外掛、針對生產環境的一些優化措施放置在裡面。

環境變數

webpack的環境變數跟作業系統的環境變數是不同的,webpack允許我們傳入任意數量的環境變數,通過命令列--env傳入。但是若要使用該環境變數,需要將module.exports變為函式,將環境變數以引數的形式傳入。

// webpack.config.js
const path = require('path');

module.exports = env => {
  // 使用你的環境變數
  console.log('NODE_ENV: ', env.NODE_ENV);
  console.log('Production: ', env.production);

  return {
    entry: './src/index.js',
    output: {
      filename: 'bundle.js',
      path: path.resolve(__dirname, 'dist')
    }
  };
};
複製程式碼

通過這樣,我們便可以區分出不同的環境需要的不同的配置。

擴充

HappyPack

webpack允許執行在node.js中,由於它是單執行緒模型,因此不能並行處理多個任務,Happy Pack將任務分解給多個子程式去併發執行,子程式處理完成後再將結果傳送給主程式,因此能實現讓webpack同時處理多個任務,減少構建時間。

const HappyPack = require('happypack');

module.exports = {
    module: {
        rules: [
            {
                test: /\.js$/,
                // 將對.js檔案的處理轉交給id為babel的HappyPack的實列
                use: ['happypack/loader?id=babel'],
                exclude: path.resolve(__dirname, 'node_modules') // 排除檔案
            }
        ]
    },
    plugins: [
    /****   使用HappyPack例項化    *****/
        new HappyPack({
            // 用唯一的識別符號id來代表當前的HappyPack 處理一類特定的檔案
            id: 'babel',
            // 處理檔案,用法和Loader配置是一樣的
            loaders: ['babel-loader']
        })
    ]
}
複製程式碼

在loader中,將對檔案的處理都傳遞給happypack/loader,利用id作為識別符號。然後在plugin外掛中新增HappyPack例項,告訴HappyPack需要做的事情。 HappyPack是為了解決webpack在node中單執行緒執行構建速度慢而存在的一個外掛,能夠顯著的提高webpack的構建速度。

總結

在開發環境中,我們更注重的是如何及時快速的將更新的程式碼展示在網頁上,如何快速定位錯誤,因此webpack提供了強效source-map和模組熱更新的機制來幫助開發人員。在生產環境中,我們需要的是如何讓程式碼包更小、構建更迅速,因此有了輕量級source-map,css程式碼壓縮工具,tree shaking配和js壓縮工具等等,都是為了讓最後打包出來的程式碼體量更小。 同樣在專案較為複雜的時候也需要將程式碼進行分包處理,使用非同步載入,進一步提高使用者使用時的載入速度,同樣為了彌補webpack在node中也是單執行緒載入的原因,還提供了HappyPack,使得webpack能夠並行載入。

相關文章