趁webpack5還沒出,先升級成webpack4吧


上一次將webpack1升級到3,也僅是 半年前,前端工具發展變化太快了,如今webpack4已經灰常穩定,傳說性能提升非常高,值得升級。

一直用着的webpack3越來越慢,一分多鍾的編譯時間簡直不能忍,升級之后在幾個系統和幾台電腦上評測,平均提高了7-9倍,生產模式的最突出

 

升級之后完整的 webpack4項目配置DEMO  已經放到Github中,歡迎圍觀 star ~

關於如何升級到V4已經有很多優秀的文章,結合官方文檔你也可以升級起來

本文僅說說本次升級主要做的改動優化點,或者坑

 

webpack4升級完全指南    webpack4 changelog   React 16 加載優化性能

 

1. 移除了commonchunk插件,改用了optimization屬性進行更加靈活的配置 ,不過稍微不注意,就會有問題,如

Uncaught Error: only one instance of babel-polyfill is allowed

 

如果一個頁面存在多個entry入口文件,即頁面引用了多個模塊時,默認會產生多個獨立的common區

所以記得將common設為公有,如

optimization: {
        runtimeChunk: {
            name: 'common'
        },

2. 默認的生產模式noEmitOnError為true,導致代碼檢查工具報錯之后無法將檢查結果寫入文件中

按需將其設置為false即可

optimization: {
        noEmitOnErrors: false,

3. 默認的提取公共模塊機制可能會產生意外的結果,盡量取消默認后再自定義

在多頁面應用中,假設某個頁面的css文件重寫了樣式,就有可能使這個重寫流入到公共樣式中,在另一個頁面被引用而導致布局出錯。這時樣式是不需要提取出來的,除非特殊情況

比如可以將default設置為false,或者表現得更強烈一點

optimization: {
        splitChunks: {
            chunks(chunk) {
                // 不需要提取公共代碼的模塊
                return !(configs.commonChunkExcludes || []).includes(chunk.name);
            },
            name: 'common',
            minChunks: 2,
            cacheGroups: {
                default: false,
                styles: {
                    name: 'common',
                    test: /\.scss|css$/,
                    chunks: 'initial',
                    // 不生成公共樣式文件
                    minChunks: 999999,
                    enforce: true
                }
            }
        }

4. 將css文件提取的 ExtractTextWebpackPlugin 插件 替換成 mini-css-extract-plugin

 升級指南里說着這個新插件不兼容web-dev-server,不過目前還沒遇到,碰到的幾個坑開始以為是它提取出的問題,后來發現並不是..

5. 正確地使用 optimization.concatenateModules ,需要關閉babel的module模塊轉換

6. 看起來似乎 loader 的 exclude 和 include 配置失效了,不知道是為何

7. 加入編譯結果消息彈出提示,更友好,引入 webpack-build-notifier

長長的編譯結果,看起來很乏味,開發人員並不能知道什么時候編譯好了

new WebpackBuildNotifierPlugin({
            title: processEntity,
            suppressSuccess: false,
            suppressCompileStart: false,
            suppressWarning: false,
            activateTerminalOnError: true
        }),

    

在win10上看比較醒目直觀,但在win7上僅是狀態欄的氣泡彈出

不過在編譯結果的內容提示還不夠完善,可以改進 

 

8. webpack-dev-server的端口自動獲取空閑端口,多webpack項目共存時很方便

因基本所有獲取空閑端口的npm包都是異步的,原理都是以端口開啟服務器,如果開啟成功則表示這個端口空閑。

但項目的webpack配置是直接 module.export一個配置項的,不是使用NodeJS API的方式,嘗試切換為這種方式時發現竟然與HMR不同兼容,就此作罷

嘗試尋找同步直接獲取空閑端口的辦法,想出了一個簡單的,直接執行 netstat -an 命令列出當前進程端口再正則匹配即可,奈思~

 1 let execSync = require('child_process').execSync,
 2     // 已使用的端口
 3     usedPorts = [],
 4     // (初始)可使用的端口
 5     freePort = 10000,
 6     // 可用端口范圍
 7     portStart = 10000;
 8     portEnd = 30000,
 9     // 查詢最大步
10     maxStep = 100000;
11 
12 /**
13  * 獲取隨機端口
14  * @return {[type]} [description]
15  */
16 function getRandomPort() {
17     return Math.floor(Math.random() * (portEnd - portStart) + portStart);
18 }
19 
20 function getFreePort() {
21     console.log('Finding free port...');
22 
23     let stepIndex = 0,
24         res = '',
25         portSplitStr = ':';
26 
27     try {
28         res = execSync('netstat -an', {
29             encoding: 'utf-8'
30         });
31         usedPorts = res.match(/\s(0.0.0.0|127.0.0.1):(\d+)\s/g);
32 
33         if (!usedPorts) {
34             usedPorts = res.match(/\s(\*|127.0.0.1)\.(\d+)\s/g);
35             portSplitStr = '.';
36         }
37 
38         usedPorts = usedPorts.map(item => {
39             let port = item.split(portSplitStr);
40             port = port.slice(-1)[0];
41             return parseInt(port.slice(0, -1), 10);
42         });
43 
44         usedPorts = [...new Set(usedPorts)];
45 
46         let portAvaliable = false;
47         while (!portAvaliable) {
48             freePort = getRandomPort();
49 
50             if (!usedPorts.includes(freePort)) {
51                 portAvaliable = true;
52                 console.log('Use port ' + freePort + ' for devServer\n');
53             }
54 
55             if (++stepIndex > maxStep) {
56                 console.log('Cannot find free port for devServer\n');
57                 break;
58             }
59         }
60     } catch(e) {
61         console.log('Cannot find free port for devServer\n');
62         console.log(e);
63     }
64 
65     return freePort;
66 }
67 
68 module.exports = getFreePort;
View Code

 

9. 編譯的dos進程窗添加標題,多個webpack項目執行時,在任務欄小窗區分更方便

也比較簡單,直接設置即可

process.title = `${configs.versionControl || 'branch'}--${configs.name || 'anonymous'}--[${process.env.NODE_ENV}]`,

不過在使用git bash時,這樣設置是無效的

使用 node-bash-title 即可

require('node-bash-title')(`${configs.versionControl || 'branch'}--${configs.name || 'anonymous'}--[${process.env.NODE_ENV}]`);

10. 引入 dllplugin動態鏈接庫方案,將第三方庫單獨打包,再鏈入我們的webpack項目中

可以參考介篇文章

新建一個webpack.dll.config.js配置文件

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

module.exports = {
    entry: {
        // 需要預配置動態鏈接的庫
        vendor: [
            'babel-polyfill',
            // 'echarts'
            'react',
            // 'redux',
            // 'react-redux',
            'react-dom',
            // 'react-router'
        ]
    },

    // 啟用sourceMap
    // devtool: 'cheap-module-source-map',

    output: {
        path: path.resolve(__dirname, './'),
        filename: '[name].js',
        library: '[name]_library_wcr'
    },

    plugins: [
        new webpack.DllPlugin({
            path: path.join(__dirname, './', '[name].manifest.json'),
            name: '[name]_library_wcr'
        })
    ]
}

執行一次,將第三方包打包出來,如果該配置文件有改動的,也需要再次打包

使用 DllReferencePlugin 插件鏈接這個manifest清單引用

new webpack.DllReferencePlugin({
            manifest: require(path.join(__dirname, './dll/', 'vendor.manifest.json')),
        }),

使用 add-asset-html-webpack-plugin 這個插件將vendor庫插入到頁面中

需要注意的是,默認它會將vendor插入到所有htmlWebpackPlugin設置的頁面中,所有我們需要通過files屬性定義好

如果有父頁面的,則只插入生成的父頁面中即可

// 動態鏈接庫引用配置
if (configs.vendorDllOpen) {
    let addAssetHtmlPluginOption = {
        filepath: require.resolve('./dll/vendor.js'),
        includeSourcemap: false,
        hash: true
    };

    if (configs.vendorDllInsertFiles !== 'all') {
        Object.assign(addAssetHtmlPluginOption, {
            files: configs.vendorDllInsertFiles
        });
    }

    commonConfig.plugins.push(
        new webpack.DllReferencePlugin({
            manifest: require(path.join(__dirname, './dll/', 'vendor.manifest.json')),
        }),
        new AddAssetHtmlPlugin(addAssetHtmlPluginOption)
    );
}

不過它的hash控制不能設定位數,不夠優雅

注意這里是由 htmlWebpackPlugin調用的ejs-loader 解析源頁面文件的配置生成的

<% for(var key in htmlWebpackPlugin.files.js) { %>
    <script src="<%= htmlWebpackPlugin.files.js[key] %>"></script>
    <% } %>

 

11. 使用 webpack-bundle-analyzer  分析打包結果

// 打包模塊分析
if (process.argv.includes('--analysis')) {
    commonConfig.plugins.push(new BundleAnalyzerPlugin({
        analyzerMode: 'server',
        analyzerHost: '127.0.0.1',
        analyzerPort: require('./getFreePortSync')(),
        reportFilename: 'report.html',
        defaultSizes: 'parsed',
        openAnalyzer: true,
        generateStatsFile: false,
        statsFilename: 'stats.json',
        statsOptions: null,
        logLevel: 'info'
    }));
}

 

 12. 引入代碼檢查工具套件,關於這部分,可移步 這里

 

 13. 將配置文件再抽取,抽出核心部分與和業務相關的多變動的部分

形成如下結構,一般來說只需要變動 webpack.config.js 這個配置即可

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM