面試題:webpack之效能優化

李赫feixuan發表於2019-04-07

移除外掛某些包,減少外掛體積

比如說通用的日期庫是moment.js,這個庫會佔用很大的體積,因為當引用這個庫的時候,所有的locale檔案都被引入,而這些檔案甚至在整個庫的體積中佔了大部分, 要想對此進行優化必須在webpack打包時移除這部分內容

webpack自帶的IgnorePlugin外掛便會處理進行優化

plugins:[    
    new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
]複製程式碼

以上配置忽略了時間格式化moment.js中的語言包

優化前

面試題:webpack之效能優化

優化後

面試題:webpack之效能優化


模組化引入 按需匯出

我們在引入lodash這種重型的js庫的時候,我們可能只需要引用裡面部分函式,但是當我們全部引入打包lodash.js體積就會很龐大

優化前

面試題:webpack之效能優化

import _ from 'lodash';

console.log(_.map)
//ƒ map(collection,iteratee){var func=isArray(collection)?arrayMap:baseMap;return func(collection,getIteratee(iteratee,3));}複製程式碼

但是我們如果按需匯入 這樣就會減少很大體積

優化後

面試題:webpack之效能優化

import map from 'lodash/map';

console.log(map)
//ƒ map(collection, iteratee) {
  var func = isArray(collection) ? arrayMap : baseMap;
  return func(collection, baseIteratee(iteratee, 3));
}複製程式碼


優化resolve.extensions配置 設定解析檔案字尾

設定解析檔案字尾,可以加速檔案尋找速度
resolve: {
    extensions: ['.js', '.json', 'jsx']
}複製程式碼


優化resolve.modules配置

 resolve.modules 用於配置Webpack去哪些目錄下尋找第三方模組。resolve.modules的預設值是[node modules],含義是先去當前目錄的/node modules目錄下去找我們想找的模組,如果沒找到,就去上一級目錄../node modules中找,再沒有就去../ .. /node modules中找,以此類推,這和Node.js的模組尋找機制很相似。當安裝的第三方模組都放在專案根目錄的./node modules目錄下時,就沒有必要按照預設的方式去一層層地尋找,可以指明存放第三方模組的絕對路徑,以減少尋找。  
resolve: {
    // 使用絕對路徑指明第三方模組存放的位置,以減少搜尋步驟
    modules: [path.resolve(__dirname,'node_modules')]
}複製程式碼

優化loader配置 縮小檔案搜尋範圍

由於Loader對檔案的轉換操作很耗時,所以需要讓儘可能少的檔案被Loader處理。我們可以通過以下3方面優化Loader配置: 
(1)優化正則匹配 
(2)通過cacheDirectory選項開啟快取 
(3)通過include、exclude來減少被處理的檔案。 
module: {
    rules: [
        {
            test:/\.js$/,
            //babel-loader支援快取轉換出的結果,通過cacheDirectory選項開啟
            loader:'babel-loader?cacheDirectory',
            //只對專案根目錄下的src 目錄中的檔案採用 babel-loader
            include: [path.resolve('src')],
            //排除 node_modules 目錄下的檔案,node_modules 目錄下的檔案都是採用的 ES5 語法,沒必要再通過 Babel 去轉換
            exclude: path.resolve(__dirname, 'node_modules')
        }
    ]
}複製程式碼

optimization.splitChunks 提取公共程式碼

Webpack 4 移除了 CommonsChunkPlugin取而代之的是兩個新的配置項 optimization.splitChunks 和 optimization.runtimeChunk。

CommonsChunkPlugin

webpack 將多個模組打包之後的程式碼集合稱為 chunk。為了將一些很少變化的常用庫(react、redux、lodash)與業務程式碼分開,或者是一些不同入口共同使用的公共模組,開發者常常需要將它們單獨打包,這些都可以通過配置 CommonsChunkPlugin 來實現。

entry: {
    app:'./main.js',
    vendor: ['react','react-dom']
},
plugins:
    new webpack.optimize.CommonsChunkPlugin({names:['vendor']})
]

複製程式碼

webpack 4 準備通過 optimization.splitChunks 和 optimization.runtimeChunk 屬性來簡化程式碼分割的配置

optimization.splitChunks

通過設定 optimization.splitChunks.chunks: "all" 來啟動預設的程式碼分割配置項。  

當滿足如下條件時,webpack 會自動打包 chunks: 

1)當前模組是公共模組(多處引用)或者模組來自 node_modules 

2)當前模組大小大於 30kb 如果此模組是按需載入,並行請求的最大數量小於等於 5 

3)如果此模組在初始頁面載入,並行請求的最大數量小於等於 3

optimization: {
    splitChunks: {
        chunks: 'all',
        name: true,
        automaticNameDelimiter: '-',  // 模組間的連線符,預設為"~"
        cacheGroups: {
            vendors: {
                test: /[\\/]node_modules[\\/]/,
                priority: -10  // 優先順序,越小優先順序越高
            },
            default: {  // 預設設定,可被重寫
                minChunks: 2,
                priority: -20,
                reuseExistingChunk: true  // 如果本來已經把程式碼提取出來,則重用存在的而不是重新產生
            }
        }
    }
}複製程式碼

面試題:webpack之效能優化

用 Happypack 來加速程式碼構建

由於有大量檔案需要解析和處理,構建是檔案讀寫和計算密集型的操作,特別是當檔案數量變多後,Webpack 構建慢的問題會顯得嚴重。 
執行在 Node.js 之上的 Webpack 是單執行緒模型的,也就是說 Webpack 需要處理的任務需要一件件挨著做,不能多個事情一起做。 檔案讀寫和計算操作是無法避免的,那能不能讓 Webpack 同一時刻處理多個任務,HappyPack 就能讓 Webpack 做到這點,它把任務分解給多個子程式去併發的執行,子程式處理完後再把結果傳送給主程式。
當同時讀取多個loader檔案資源時,比如`babel-loader`需要 transform 各種jsx,es6的資原始檔。在這種同步計算同時需要大量耗費 cpu 運算的過程中,node的單程式模型就無優勢了,而 Happypack 就是針對解決此類問題而生的存在
const HappyPack = require('happypack');
module: {    rules: [
        {
            test:/\.js/,
            //loader:'babel-loader',
            include: [path.resolve('src')],
            // 排除 node_modules 目錄下的檔案,node_modules 目錄下的檔案都是採用的 ES5 語法,沒必要再通過 Babel 去轉換
            exclude: path.resolve(__dirname, 'node_modules'),
            // 把對 .js 檔案的處理轉交給 id 為 babel 的 HappyPack 例項
            loader: 'happypack/loader?id=babel'
        }
    ]
},
plugins:[    new HappyPack({
        // 用唯一的識別符號 id 來代表當前的 HappyPack 是用來處理一類特定的檔案
        id: 'babel',
        // 如何處理 .js 檔案,用法和 Loader 配置中一樣
        loaders: ['babel-loader?cacheDirectory'],
        // ... 其它配置項
    })
]

複製程式碼

未完待續................


相關文章