趁webpack5还没出,先升级成webpack4吧

上一次将webpack1升级到3,也仅是 半年前,前端工具发展变化太快了,如今webpack4已经灰常稳定,传说性能提升非常高,值得升级。 一直用着的webpack3越来越慢,一分多钟的编译时间简直不能忍,升级之后在几个系统和几台电脑上评测,平均提高了7-9倍,生产模式的最突出

升级之后完整的 webpack4项目配置DEMO 

关于如何升级到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;

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 这个配置即可

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏贾鹏辉的技术专栏@CrazyCodeBoy

React Native应用部署/热更新-CodePush最新集成总结(新)

React Native应用部署/热更新-CodePush最新集成总结(新) ---- 更新说明: 此次博文更新适配了最新版的CodePush v1.17....

87260
来自专栏大数据钻研

HTML meta标签总结与属性使用介绍

之前学习前端中,对meta标签的了解仅仅只是这一句。 <metacharset="UTF-8"> 但是打开任意的网站,其head标签内都有一列的meta标签。比...

34660
来自专栏Seebug漏洞平台

Bypass unsafe-inline mode CSP

[+] Author: evi1m0 [+] Team: n0tr00t security team [+] From: http://www.n0tr0...

35340
来自专栏Linux驱动

23.QT记事本

源码下载地址: https://download.csdn.net/download/qq_37997682/10453294

15930
来自专栏Micro_awake web

同源策略和跨域解决方法

第一部分:同源策略:same-origin policy 1.同源策略的由来: 1995年,同源策略由Netscape(曾经的浏览器霸主,拒绝微软收购请求,被I...

32670
来自专栏老马寒门IT

Node入门教程(7)第五章:node 模块化(下) npm与yarn详解

Node的包管理器 JavaScript缺少包结构的定义,而CommonJS定义了一系列的规范。而NPM的出现则是为了在CommonJS规范的基础上,实现解决包...

37560
来自专栏BIT泽清

React Native应用部署/马甲包热更新-CodePush最新集成总结(2018年最新)

React Native支持大家用React Native技术开发APP,并打包生成一个APP。在动态更新方面React Native只是提供了动态更新的基础,...

43900
来自专栏FreeBuf

闲话文件上传漏洞

文件上传漏洞是web安全中经常利用到的一种漏洞形式。这种类型的攻击从大的类型上来说,是攻击 数据与代码分离原则 的一种攻击。 一些web应用程序中允许上传图片,...

48970
来自专栏JavaEE

Intellij IDEA 使用教程

正所谓工欲善其事,必先利其器,身为码农的我们,拥有得心应手的编辑器就好比如鱼得水,在万行代码中取bug首级就如观鱼赏花!IDEA就堪称是当世之神兵,自诩为最智能...

12920
来自专栏云计算教程系列

如何在Ubuntu 14.04中使用NodeJS,SailsJS和DustJS构建SPA(单页应用程序)

Node.js®是一个基于Chrome JavaScript运行时的平台,可轻松构建快速,可扩展的网络应用程序。Node.js使用事件驱动的非阻塞I / O模型...

15100

扫码关注云+社区

领取腾讯云代金券