最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Webpack打包性能优化实践

    正文概述 掘金(Jackcaoss)   2021-07-04   567

    前言

    我觉得webpack的配置对于很多前端来说就是一个黑洞,首先是配置极多,其次项目大起来之后优化起来总是无从下手,后来自己通过看了众多的打包性能优化的配置之后总结下来其实就这么几点预编译,做缓存,多线程,少检索

    在聊到性能优化前,我们先得知道怎么去分析我们打包性能。

    性能分析工具

    性能分析主要分为打包大小分析打包时间分析

    打包大小分析

    打包的大小分析的插件webpack-bundle-analyzer,这个插件可以将我们打包的各模块的大小可视化的展示出来了。

    首先我们在package.json中写一个命令:

    // package.json
    "scripts": {
        "analyzer": "npm run build && webpack-bundle-analyzer --port 8888 ./dist/analyzer.json"
    }
    

    webpack.prod.js代码:

    // webpack.prod.js
    const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
    ...
    
    plugins:{
        ...
        new BundleAnalyzerPlugin({
          analyzerMode: 'disabled',
          generateStatsFile: process.env.Analyzer === 'on',
          statsFilename: path.join(__dirname, '../', 'dist/analyzer.json')
        })
    }
    

    效果如图所示:

    Webpack打包性能优化实践 当然我们得到了我们项目模块的大小详情之后,我们可以优化打出来比较大的模块,比如lodash,我们并不需要把所有的方法都打进来的,可以通过只提取使用到的方法即可。

    打包时间分析

    当然webpack打包我们除了要知道所有的打包出来的模块大小,还需要分析每个打包环节所花的时间,从而优化不同环节的耗时操作,对此我们要下载speed-measure-webpack-plugin插件。

    其实配置很简单,只需要在我们的webpack配置包一层即可。

    const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
    
    const smp = new SpeedMeasurePlugin();
    
    module.exports = smp.wrap(webpackConfig) // webpackConfig指打包配置
    

    各个环节所消耗的时间如下:

    Webpack打包性能优化实践

    多线程

    HappyPack

    由于构建需要对大量文件进行解析和处理,所以构建是文件读写和计算密集型操作。当文件增多的时候,webpack构建速度会越来越慢,因为webpack是运行在Node.js的单线程模型,所以webpack在构建时只能一个一个处理任务,无法一次性处理多个任务。

    让webpack支持多线程的话有两个方式:HappyPack(不维护)和thread-loader

    HappyPack配置

    const HappyPack = require('happypack');
    const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
    
    module.exports = {
        rules: [
          {
            test: /\.jsx?$/,
            exclude: /node_modules/,
            loader: 'happypack/loader?id=jsx'
          },
        ],
        plugins: [
          new HappyPack({
            id: 'jsx',
            threadPool: happyThreadPool,
            loaders: [
              {
                loader: 'babel-loader',
                options: {
                  cacheDirectory: '.webpack_cache'
                }
              }
            ]
          })
        ]
    }
    

    这边我们使用babel-loader时使用指定的目录将用来缓存 loader 的执行结果。之后的 webpack 构建,将会尝试读取缓存,来避免在每次执行时,可能产生的、高性能消耗的 Babel 重新编译过程。

    当然并不是所有的loader都需要用happypack开启多线程,因为多线程本身的启动也是需要时间的,项目不大或者不是耗时的loader可选择不开启。

    thread-loader

    thread-loader配置: thread-loader配置相比于happypack会更加简单。

    module.exports = {
        module: {
            rules: [
                {
                    test: /\.jsx?$/,
                    use: ['thread-loader', 'babel-loader']
                }
            ]
        }
    }
    

    缓存

    cache-loader

    因为每次webpack打包编译会把所有的文件重新打包编译一遍,这也意味着很多文件没有修改也会重新编译,实际上这样也会导致构建时间的增多,在性能开销较大的loader,可以用cache-loader将结果缓存下来。

    module.exports = {
        module: {
            rules: [
                {
                    test: /\.jsx?$/,
                    use: ['cache-loader', 'babel-loader']
                }
            ]
        }
    }
    

    上文我们看到其实babel-loader自带了缓存功能,但是可能有些loader没有这种配置,可使用cache-loader缓存编译结果。

    基础模块抽离

    webpack-dll-pllugin

    基础模块抽离主要就是将一些不会经常变更的第三方依赖,单独抽离出来。例如我们在项目里面常用的react全家桶,lodash等。

    实际上我们每次打包都要去编译这些几乎不需要变更的第三方依赖库,这会导致我们浪费很多时间,我们可以使用webpack-dll-plugin库以一种预编译的方式,将这些基础模块提前打包成一个个动态链接库(一个链接库可包括多个模块),之后每次打包的时候就不用再去编译这些基础库,只要这些第三方依赖库的版本没有改变,我们就不需要重新去编译。

    externals

    除了可以使用webpack-dll-plugin去将基础库进行预编译,还可以使用CDN引入这些库,并配合webpackexternals配置不将这些库打包进去以优化构建速度。

    index.html:

    <script
      src="https://code.jquery.com/jquery-3.1.0.js"
      crossorigin="anonymous"
    ></script>
    

    webpack.config.js:

    module.exports = {
        //...
        externals: {
            'jquery': 'jQuery'
        }
    }
    

    这样通过import $ from 'jquery';依然可以使用jquery。

    缩小文件搜索范围

    resolve.modules

    由于webpack搜索第三方依赖库,先会搜索./node_modules,然后没搜索到会继续往上一层../node_modules,以此内推。因此我们可以指定好第三方依赖库的路径,减少搜索时间。

    const path = require('path');
    module.exports = {
        //...
        resolve: {
            modules: [path.resolve(__dirname, 'node_modules')],
        }
    }
    

    resolve.extensions

    当我们导入的文件没有后缀的时候,我们可以通过指定resolve.extensions来告诉webpack的后缀的搜索顺序,一般频率最高的放在最前面以此来减少搜索次数。

    module.exports = {
        //...
        resolve: {
            extensions: ['jsx','js','json']
        }
    }
    

    module.noParse

    由于一些如jquerychartjs等库没有实现模块化标准,这样解析这些库会浪费时间而且没有意义。

    module.exports = {
        //...
        module: {
            noParse: /jquery/
        }
    }
    

    loader下的exclude和include

    webpackloader编译模块时,我们可以指定exclude和include属性,exclude表示哪些文件夹下的模块不需要编译,include表示哪些文件夹下模块需要编译,两者同样使用的是绝对路径。

    const path = require('path');
    module.exports = {
        //...
        module: {
            rules: [
                {
                    test: /\.js[x]?$/,
                    use: ['babel-loader'],
                    exclude: [path.resolve(__dirname, 'node_modules')]
                }
            ]
        },
    }
    

    参考:

    深入浅出 Webpack


    起源地下载网 » Webpack打包性能优化实践

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元