Webpack打包構建太慢了?試試幾個方法

来源:http://www.cnblogs.com/imwtr/archive/2017/11/07/7801973.html
-Advertisement-
Play Games

Webpack是個很流行的打包工具,但其打包速度卻一直被吐槽著 如果不用上一些打包的優化建議,單單打包兩三個文件就能花上好幾秒,放上幾十個入口文件依賴幾百上千個包的話,幾分鐘十幾分鐘妥妥的 本文整理了常見的一些方法,部分使用之後就看到了很大改善,部分沒什麼明顯的變化,也可能是項目規模還不夠大,先記錄 ...


Webpack是個很流行的打包工具,但其打包速度卻一直被吐槽著

如果不用上一些打包的優化建議,單單打包兩三個文件就能花上好幾秒,放上幾十個入口文件依賴幾百上千個包的話,幾分鐘十幾分鐘妥妥的

本文整理了常見的一些方法,部分使用之後就看到了很大改善,部分沒什麼明顯的變化,也可能是項目規模還不夠大,先記錄一下方法也好

 

一、使用監聽模式或熱更新熱替換

webpack支持監聽模式,此時需要重新編譯時就可以進行增量構建,增量構建是很快的,基本不到一秒或幾秒之內就能重新編譯好

註意區分一下開發環境和線上環境,開發環境啟用熱更新替換

// 開發環境設置本地伺服器,實現熱更新
    devServer: {
        contentBase: path.resolve(__dirname, 'static'),
        // 提供給外部訪問
        host: '0.0.0.0',
        port: 8388,
        // 允許開發伺服器訪問本地伺服器的包JSON文件,防止跨域
        headers: {
            'Access-Control-Allow-Origin': '*'
        },
        // 設置熱替換
        hot: true,
        // 設置頁面引入
        inline: true
    },

    // 文件輸出配置
    output: {
        // 設置路徑,防止訪問本地伺服器相關資源時,被開發伺服器認為是相對其的路徑
        publicPath: 'http://localhost:8188/dist/js/',
    },


// 插件配置
    plugins: [
        // 熱更新替換
        new webpack.HotModuleReplacementPlugin()
    ]

線上環境的編譯,加個 --watch 參數就可以了

 

二、開發環境不做無意義的操作

很多配置,在開發階段是不需要去做的,我們可以區分出開發和線上的兩套配置,這樣在需要上線的時候再全量編譯即可

比如說 代碼壓縮、目錄內容清理、計算文件hash、提取CSS文件等

 

三、選擇一個合適的devtool屬性值

配置devtool可以支持使用sourceMap,但有些是耗時嚴重的,這個得多試試

 

四、代碼壓縮用ParallelUglifyPlugin代替自帶的 UglifyJsPlugin插件

自帶的JS壓縮插件是單線程執行的,而webpack-parallel-uglify-plugin可以並行的執行,在我的小demo中使用後,速度直接從25s變成了14s

      new webpack.optimize.UglifyJsPlugin({
            sourceMap: true,
            compress: {
                warnings: false
            }
        }),



ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin')

new ParallelUglifyPlugin({
           cacheDir: '.cache/',
           uglifyJS:{
             output: {
               comments: false
             },
             compress: {
               warnings: false
             }
           }
         }),

 

五、css-loader使用0.15.0以下的版本

聽聞這個版本以上的速度會慢許多,不過在我的小demo中還沒看到明顯變化

 

六、使用fast-sass-loader代替sass-loader

fast-sass-loader可以並行地處理sass,在提交構建之前會先組織好代碼,速度也會快一些

 

七、babel-loader開啟緩存

babel-loader在執行的時候,可能會產生一些運行期間重覆的公共文件,造成代碼體積大冗餘,同時也會減慢編譯效率

可以加上cacheDirectory參數或使用 transform-runtime 插件試試

// webpack.config.js
use: [{
                loader: 'babel-loader',
                options: {
                    cacheDirectory: true
                }]


// .bablerc
{
    "presets": [
        "env",
        "react"
    ],
    "plugins": ["transform-runtime"]
}

 

八、不需要打包編譯的插件庫換成全局<script>標簽引入的方式

比如jQuery插件,react, react-dom等,代碼量是很多的,打包起來可能會很耗時

可以直接用標簽引入,然後在webpack配置里使用 expose-loader  或 externalsProvidePlugin  提供給模塊內部使用相應的變數

// @1
use: [{
                loader: 'expose-loader',
                options: '$'
            }, {
                loader: 'expose-loader',
                options: 'jQuery'
            }]


// @2
externals: {
        jquery: 'jQuery'
    },


// @3
        new webpack.ProvidePlugin({
            $: 'jquery',
            jQuery: 'jquery',
            'window.jQuery': 'jquery'
        }),

 

九、使用 DllPlugin 和 DllReferencePlugin 

這種方式其實和externals是類似的,主要用於有些模塊沒有可以在<script>標簽中引入的資源(純npm包)

Dll是動態鏈接庫的意思,實際上就是將這些npm打包生成一個JSON文件,這個文件里包含了npm包的路徑對應信息

這兩個插件要一起用

首先,新建一個dll.config.js配置文件,先用webpack來打包這個文件

const webpack = require('webpack');
const path = require('path');

module.exports = {
    output: {
        // 將會生成./ddl/lib.js文件
        path: path.resolve(__dirname, 'ddl'),
        filename: '[name].js',
        library: '[name]',
    },
    entry: {
        "lib": [
            'react',
            'react-dom',
            'jquery'
            // ...其它庫
        ],
    },
    plugins: [
        new webpack.DllPlugin({
            // 生成的映射關係文件
            path: 'manifest.json',
            name: '[name]',
            context: __dirname,
        }),
    ],
};

manifest.json文件中就是相應的包對應的信息

然後在我們的項目配置文件中配置DllReferencePlugin 使用這個清單文件

    // 插件配置
    plugins: [
        new webpack.DllReferencePlugin({
            context: __dirname,
            manifest: require('./manifest.json')
        }),

 

十、提取公共代碼

使用CommonsChunkPlugin提取公共的模塊,可以減少文件體積,也有助於瀏覽器層的文件緩存,還是比較推薦的

 // 提取公共模塊文件
        new webpack.optimize.CommonsChunkPlugin({
            chunks: ['home', 'detail'],
            // 開發環境下需要使用熱更新替換,而此時common用chunkhash會出錯,可以直接不用hash
            filename: '[name].js' + (isProduction ? '?[chunkhash:8]' : ''),
            name: 'common'
        }),




// 切合公共模塊的提取規則,有時後你需要明確指定預設放到公共文件的模塊
// 文件入口配置
    entry: {
        home: './src/js/home',
        detail: './src/js/detail',
        // 提取jquery入公共文件
        common: ['jquery', 'react', 'react-dom']
    },

 

十一、使用HappyPack來加速構建

HappyPack會採用多進程去打包構建,使用方式還是蠻簡單的,但並不是支持所有的loader

首先引入,定義一下這個插件所開啟的線程,推薦是四個,其實也可以直接使用預設的就行了

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

然後在module的規則里改動一下,引入它,其中 id是一個標識符

{
            test: /\.jsx?$/,
            // 編譯js或jsx文件,使用babel-loader轉換es6為es5
            exclude: /node_modules/,
            loader: 'HappyPack/loader?id=js'
            // use: [{
            //     loader: 'babel-loader',
            //     options: {

            //     }
            // }]
        }

然後我們調用插件,設置匹配的id,然後相關的配置可以直接把use:的規則部分套在loaders上

new HappyPack({
            id: 'js',
            loaders: [{
                loader: 'babel-loader',
                options: {
                    // cacheDirectory: true
                }
            }]
        }),

要註意的第一點是,它對file-loaderurl-loader支持不好,所以這兩個loader就不需要換成happypack了,其他loader可以類似地換一下

要註意的第二點是,使用ExtractTextWebpackPlugin提取css文件也不是完全就能轉換過來,所以需要小小的改動一下,比如

module: {
        rules: [{
            test: /\.css$/,
            // loader: 'HappyPack/loader?id=css'
            // 提取CSS文件
            use: cssExtractor.extract({
                // 如果配置成不提取,則此類文件使用style-loader插入到<head>標簽中
                fallback: 'style-loader',
                use: 'HappyPack/loader?id=css'
                // use: [{
                //         loader: 'css-loader',
                //         options: {
                //             // url: false,
                //             minimize: true
                //         }
                //     },
                //     // 'postcss-loader'
                // ]
            })
        }, {
            test: /\.scss$/,
            // loader: 'HappyPack/loader?id=scss'
            // 編譯Sass文件 提取CSS文件
            use: sassExtractor.extract({
                // 如果配置成不提取,則此類文件使用style-loader插入到<head>標簽中
                fallback: 'style-loader',
                use: 'HappyPack/loader?id=scss'
                // use: [
                //     'css-loader',
                //     // 'postcss-loader',
                //     {
                //         loader: 'sass-loader',
                //         options: {
                //             sourceMap: true,
                //             outputStyle: 'compressed'
                //         }
                //     }
                // ]
            })
        }

因為它是直接函數調用的,我們就放到裡層的use規則就行了,然後配置插件即可

plugins: [
        new HappyPack({
            id: 'css',
            loaders: [{
                loader: 'css-loader',
                options: {
                    // url: false,
                    minimize: true
                }
            }]
        }),
        new HappyPack({
            id: 'scss',
            loaders: [{
                'loader': 'css-loader'
            }, {
                loader: 'fast-sass-loader',
                options: {
                    sourceMap: true,
                    outputStyle: 'compressed'
                }
            }]
        }),

 

十二、優化構建時的搜索路徑

在webpack打包時,會有各種各樣的路徑要去查詢搜索,我們可以加上一些配置,讓它搜索地更快

比如說,方便改成絕對路徑的模塊路徑就改一下,以純模塊名來引入的可以加上一些目錄路徑

還可以善於用下resolve alias別名 這個欄位來配置

還有exclude等的配置,避免多餘查找的文件,比如使用babel別忘了剔除不需要遍歷的

{
            test: /\.jsx?$/,
            // 編譯js或jsx文件,使用babel-loader轉換es6為es5
            exclude: /node_modules/,
             use: [{
                 loader: 'babel-loader',
                 options: {

                 }
             }]
        }

 

 

十三、(導出編譯JSON文件)理一下打包構建涉及的模塊,分析看有哪些包是不需要打包的,只打包需要的模塊

檢查一下代碼,看看是不是有不需要引入的模塊出現在代碼里

webpack編譯時加上參數 --json > stat.json 後,可以上傳到 webpack-analyse webpack-visualizer 等分析站點上,看看打包的模塊信息

 

十四、使用ModuleConcatenationPlugin插件來加快JS執行速度

這是webpack3的新特性(Scope Hoisting),其實是借鑒了Rollup打包工具來的,它將一些有聯繫的模塊,放到一個閉包函數裡面去,通過減少閉包函數數量從而加快JS的執行速度

 new webpack.optimize.ModuleConcatenationPlugin({

        })

 

十五、使用noParse

webpack打包的時候,有時不需要解析某些模塊的依賴(這些模塊並沒有依賴,或者並根本就沒有模塊化),我們可以直接加上這個參數,直接跳過這種解析

module: {
    noParse: /node_modules\/(jquey\.js)/
  }

 

十六、使用非同步的模塊載入

這個算是可以減小模塊的體積吧,在一定程度上也是為用戶考慮的,使用require.ensure來設置哪些模塊需要非同步載入,webpack會將它打包到一個獨立的chunk中,

在某個時刻(比如用戶點擊了查看)才非同步地載入這個模塊來執行

$('.bg-input').click(() => {
    console.log('clicked, loading async.js')

    require.ensure([], require => {

        require('./components/async2').log();
        require('./components/async1').log();
        console.log('loading async.js done');
    });
});

 

十七、以模塊化來引入

有些模塊是可以以模塊化來引入的,就是說可以只引入其中的一部分,比如說lodash

// 原來的引入方式
 import {debounce} from 'lodash';

//按模塊化的引入方式
import debounce from 'lodash/debounce';

 

 主要是整理過來的,試用了幾個方法,首次編譯的速度可以從之前半分多鐘減小到十秒左右了,當然,開啟了熱更新替換後簡直美不可言

當然還有很多方法沒整理出,這些方法是有使用場景的,並不是每個都需要用,需要在自己的項目中嘗試,結合配置它的複雜性和帶來的效應來權衡。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 近乎產品的數據訪問是基於輕量級ORM——PetaPoco,哪怕是他們最新的5.2版本也是基於PetaPoco。至於為什麼沒有選用EF這些ORM,感興趣的可以去咨詢他們的產品人員,本文就不詳細說明瞭。 關於近乎源碼下載:www.jinhusns.com ...
  • Asp.Net Web API不可以需要IIS。可以自己在主機上承載一個Web API 創建WebAPI.Server項目 創建一個控制器項目的服務端 在Nuget中添加Microsoft.AspNet.WebApi.SelfHost 創建Model與Controller 首先在服務端添加一個Pro ...
  • 前兩天做一個項目的時候,由於頁面沒有限制TextBox的輸入長度,所以,後臺直接報錯了,超出資料庫最大的長度。 資料庫的長度是按照位元組來計算的,而且不同的編碼格式,漢字占用的位元組長度又不相同,比如,我們用的是UTF8,一個漢字是3個位元組,而預設的Default,一個漢字是2個位元組。 TextBox有 ...
  • 前言 參與過幾個中小型商城系統的開發,隨著時間的增長,以及對系統的深入研究和測試,發現確實有很多值得推敲和商榷的地方(總有很多重要細節存在缺陷)。基於商城系統,無論規模大小,或者本身是否分佈架構,個人覺得最核心的一環就是下單模塊,而這裡面更相關和棘手的一些設計和問題,大多時候都涉及庫存系統。想想之... ...
  • //用戶去銀行取錢(發佈方),用戶收到簡訊提醒,(接收方) class Program { static void Main(string[] args) { Bank b = new Bank(); //實例化銀行 User xm = new User("... ...
  • 第一階段: 1、用PowerDesign建數據模型,並導出SQL文件; 2、將SQL文件導入到MySQL客戶端,建立表格; MySQL數據遠程訪問:GRANT ALL PRIVILEGES ON *.* TO 'root'@'%'IDENTIFIED BY 'mypassword' WITH GRA ...
  • docker已經用了兩年多了,從開始的簡單應用到現在的自動化部署,已經越來越感覺到它的威力,今天把Hitchhiker部署完成後,看到了它與.net core項目有個類似的地方,就是對於多環境部署的時候都用到了環境變數,Hitchhiker的docker-compose.yml里有對環境地址的設置( ...
  • 今天開通博客、來1個、哇哈哈哈~~ 今天本來想複習BOM的看到定時器也算DOM一種 ?(是這樣嗎)、分享一下 參考源於:八神吻你 http://www.cnblogs.com/lmfeng/archive/2011/06/24/2089237.html 單次定時器即執行 單次延遲定時器 多次執行定時 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...