Webpack 2 中一些常見的優化措施

NO然后 7年前發布 | 26K 次閱讀 前端技術 webpack

Webpack 是一款強大的前端構建工具, 社區對其介紹的相關文章已經很多了, 本文不再贅述. 基于 Webpack 2, 本文是對我在搭建團隊前端腳手架的過程中, 搜羅的 Webpack 2 常見的優化措施的一個總結.

如果你還不了解 Webpack 2, 可以先看下 Webpack 2 快速入門

1. 分離第三方依賴

開發環境 下, 通常會采取 HMR 模式來提高開發效率. 但一般情況下, 我們只會更改自身的業務文件, 不會去更改第三方的依賴, 但 webpack 在 rebuild 的時候, 依舊會 build 所有的依賴. 因而, 為減少 rebuild 的時間, 我們可以分離第三方依賴, 在項目啟動之前, 將其單獨打包和引入.

這要借助 DllPlugin 插件.

我們定義一份生成 dll 的配置文件:

## webpacl.dll.config.js
module.exports = {
    entry: {
        vendor: [
            'vue', 'vuex', 'vue-router', 'vuex-router-sync', 'babel-polyfill', '...'
        ]
    },
    output: {
        path: path.join(__dirname, './public/', 'dist'),
        filename: '[name].dll.js',
        library: '[name]_library'
    },
    plugins: [
        new webpack.DllPlugin({
          path: path.join(__dirname, './public/', 'dist', '[name]-manifest.json'),
          name: '[name]_library'
      })
    ]
}

生成 dll 文件之后, 可以根據環境變量在頁面的靜態文件中引入:

這樣, 在每次 rebuild 的時候, webpack 都不會去重新 build vendor, 能極大減少 rebuild 的時間, 提升開發效率.

僅在開發環境下使用

2. 多進程構建

Webpack的構建過程是單進程的, 利用 HappyPack 可讓 loader 對文件進行多進程處理, 其原理圖如下:

在業務文件依賴越多和復雜的情況下, HappyPack 對 Webpack 構建效率的提升會越明顯. 下圖是我在項目使用 HappyPack 前后的一張構建時間對比圖:

HappyPack 會充分利用系統的資源來提升 Webpack 的構建效率, 所以系統本身的硬件配置會對 HappyPack 的使用有一定的影響.

HappyPack 不限于處理 js 文件, 也可以同時處理 css/vue 等其它類型文件. HappyPack 支持多個實例, 可以創建多個實例來分別處理不同的類型文件:

let HappyPack = require('happypack');
let os = require('os');
let happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
 
module.exports = {
 
 ...

 plugins: [
 new HappyPack({
 id: 'vue',
          threadPool: happyThreadPool,
          cache: true,
          verbose: true,
          loaders: ['vue-loader'],
 }),

 new HappyPack({
 id: 'js',
          threadPool: happyThreadPool,
          cache: true,
          verbose: true,
          loaders: ['babel-loader'],
 })

 # others
 ]

 ...
 
}

此外, HappyPack 同時還利用緩存來使得 rebuild 更快.

開發環境和生產環境下均可使用. 關于其原理分析, 請看 HappyPack 原理解析

3. 提取公共的依賴模塊

無論是單頁還是多頁應用, 在 生產環境 下, 通常都會利用 CommonsChunkPlugin 插件來提供公共的依賴模塊:

new webpack.optimize.CommonsChunkPlugin({
    name: "vendor",
    minChunks: ({resource}) => {
     resource && 
     resource.indexOf('node_modules') &&
     resource.match(/.js$/)
    }
}),

上述的配置會提取 node_modules 下的所有模塊, 打包出來的結果可能是這樣的:

打包結果分析圖由 webpack-bundle-analyzer 提供

這樣提取了公共模塊之后, 的確會減少業務包的大小, 但是, 這種方式會導致兩個問題:

  • 業務越復雜, 三方依賴會越多, vendor 包會越大
  • 沒有隔離業務路由組件, 所有的路由都有可能會去加載 vendor, 但并不是所有的路由組件都依賴 node_modules 下的所有模塊

所以, 上述提取公共依賴的方式不可取. 我們應該去分析業務依賴和路由, 盡可能將所有路由組件的公共依賴提取出來:

entry: {
    app: path.resolve(__dirname, '../src/page/index.js'),
    vendor: [
        'vue', 'vuex', 'vue-router', 'vuex-router-sync', 'babel-polyfill',
        'axios', '....'
    ]
},
 
new webpack.optimize.CommonsChunkPlugin({
    name: "vendor",
    filename: "vendor.js"
}),

前后兩種方式打包出來的 vendor 大小對比:

既要去提取公共依賴, 也要避免 vendor 包過于太大.

4. 文件分離

文件分離主要是將圖片和 CSS 從 js 中分離. 圖片和 CSS 都是 Webpack 需要構建的資源, 通過某種配置, 圖片可以以 base64 的方式混淆在 js 文件中, 這會增加最終的 bundle 文件的大小. 在 生產環境 下, 應該將圖片和 CSS 從 js 中分離:

  • 在生產環境下, 通過自定義插件, 將圖片的本地引用替換為 CDN 的鏈接
  • 在生產環境下, 通過 ExtractTextPlugin 來 提取 CSS.

5. 資源混淆和壓縮

Webpack提供的 UglifyJS 插件由于采用單線程壓縮, 速度比較慢,

可以使用 Parallel 插件進行優化:

let ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
let os = require('os');
 
new ParallelUglifyPlugin({
    workerCount: os.cpus().length,
    cacheDir: '.cache/',
    uglifyJS: {
        compress: {
            warnings: false,
            drop_debugger: true,
            drop_console: true
        },
        comments: false,
        sourceMap: true,
        mangle: true
    }
})

6. Gzip 壓縮

生產環境 下, 如果想進一步減小 bundle 文件的大小, 可以使用 Gzip 壓縮.

let CompressionPlugin = require("compression-webpack-plugin");
 
module.exports = {
    plugins: [
        new CompressionPlugin({
            asset: "[path].gz[query]",
            algorithm: "gzip",
            test: /.(js|html)$/,
            threshold: 10240,
            minRatio: 0.8
        })
    ]
}

Gzip 壓縮能有效減少 bundle 的文件大小:

部署上線時, 服務端也需要開啟 gzip 壓縮

7. 按需加載

在單頁應用中, 一個應用可能會對應很多路由, 每個路由都會對應一個組件; 如果將這些組件全部全部放進一個 bundle, 會導致最終的 bundle 文件比較大(看上圖的 app bundle 文件). 因而, 我們需要利用 Webpack 的 Code Splitting 功能, 將代碼進行分割, 實現路由的按需加載.

在 Vue 中, 利用 vue-router 的 懶加載 功能, 是比較容易實現按需加載的:

當訪問首頁時, 會去加載 Index 組件, 此時并不會加載 Info 組件; 只有當路由切換為 /info 時, Info 組件才會被加載.

以上是個人的一些總結, 如有不足請指正, 如有遺漏, 歡迎補充.

vue-startup 是基于上述的一些優化措施寫的一個 Vue 的腳手架, 歡迎 star.

 

來自:http://web.jobbole.com/91414/

 

 本文由用戶 NO然后 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!