zoukankan      html  css  js  c++  java
  • 趁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
    ');
    53             }
    54 
    55             if (++stepIndex > maxStep) {
    56                 console.log('Cannot find free port for devServer
    ');
    57                 break;
    58             }
    59         }
    60     } catch(e) {
    61         console.log('Cannot find free port for devServer
    ');
    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 这个配置即可

  • 相关阅读:
    程序、进程、线程区别与联系
    SQL常用知识与必须掌握的面试常问SQL语句
    快速搭建一个基于react的项目
    原生js判断设备类型
    在vue项目中设置BASE_URL
    纯前端实现数据导出excel文件
    原生js实现拖拽功能
    使用Echarts实现折线图的一点总结
    在vue项目中显示实时时间(年月日时分秒)
    在vue项目中使用MD5.js
  • 原文地址:https://www.cnblogs.com/imwtr/p/9189670.html
Copyright © 2011-2022 走看看