美文网首页
webpack构建速度优化

webpack构建速度优化

作者: Mstian | 来源:发表于2020-08-12 10:25 被阅读0次

    webpack核心概念与构建流程:

    webpack构建图
    webpack核心概念包含入口(entry)、出口(output)、处理器(loader)、插件(plugin) 、模块(module)、模式(mode)、输出文件(bundle)、组合块(chunk)。这部分内容具体可以参考webpack官网webpack中最易混淆的5个知识点

    webpack构建流程大致是进入入口文件,解析出导入语句,根据module(模块)后缀名调出对应的loader进行处理,再找该依赖所依赖的module,递归地进行编译转换,最后将编译好的module整理组合成一个或多个chunk,最终生成bundle文件,整个过程是串行进行的。

    由于项目在越来越大,构建速度也相应的越来越慢,打包体积也越来越大,因此有必要对项目进行webpack构建与打包的优化了。

    这次主要是构建速度优化,每次去运行项目的时候会看到构建时间,优化目的主要是减少构建时间。

    文章内容有如何去输出webpack构建内容分析,以及构建plugin和loader速度分析,还有对于打包的bundle文件大小关系分析。

    优化思路:

    1. 首次run的时候尽量让构建速度快

      缩小构建目标、减少文件检索范围(这块内容在下面)

    2. 二次run的时候可以使用到缓存提升构建速度

      二次缓存优化:

      eslint-loader配置cache:true
      https://www.npmjs.com/package/eslint-loader

    {
      test: /\.(js|vue)$/,
      loader: 'eslint-loader',
      enforce: 'pre',
      include: [resolve('src')],
      options: {
          formatter: require('eslint-friendly-formatter'),
          cache: true
      }
    }
    

    loader缓存会被默认缓存在node_modules/.cache/eslint-loader

    vue-loader配置cacheDirectory属性 (暂时不明确)

    {
        test: /\.vue$/,
        loader:'vue-loader',
        options: {
            cacheDirectory: path.resolve(__dirname,'node_modules/.cache')
        }
    }
    

    babel-loader配置在babel-loader中配置cacheDirectory

    {
      test: /\.js$/,
      loader: 'babel-loader?cacheDirectory', // 开启转换结果缓存
      include: [resolve('src')]
    }
    

    或者

    {
      test: /\.js$/,
      loader: 'babel-loader',
      include: [resolve('src')],
      options:{
        cacheDirectory: true
      }
    }
    

    loader缓存会被默认缓存在node_modules/.cache/babel-loader

    通过二次缓存提升构建速度有一个强大的插件(墙裂推荐,在下面插件使用中,在优化构建速度的时候可以优先考虑一下)

    插件使用

    JARVIS(可用来统计打包速度)
    JARVIS打包分析.png

    使用jarvis插件对webpack构建新能进行分析

    ERRORS AND WARNING (错误警告)

    TOTAL ASSETS SIZE(打包资源体积)

    ALL MODULES (所有打包数量)

    安装:

    npm i -D webpack-jarvis

    使用:

    const Jarvis = require("webpack-jarvis"); // 查看打包整体信息
    module.exports = {
        plugins:[
            new Jarvis({
            watchOnly: false,
            port: 3002 // optional: set a port
        }),
        ]
    }
    
    webpack-bundle-analyzer(分析打包内容体积)
    打包体积分析
    const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
    module.exports = {
      plugins:[
         new BundleAnalyzerPlugin()
      ]
    }
    
    SpeedMeasurePlugin(分析plugin以及loader的构建耗时)

    speed-measure-webpack-plugin是一个专门测试webpack构建速度的工具,可以在终端列出所有Loader和Plugin的耗时。

    SpeedMeasurePlugin.png

    安装:

    npm i -D speed-measure-webpack-plugin

    使用:使用插件的实例对象对webpack配置进行一次包裹,如代码所示,然后运行构建即可看到耗时分析

    const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
    const smp = new SpeedMeasurePlugin();
    const webpackPlugin = {
      entry:"",
      // ...
      plugins:[]
    }
    module.exports = smp.wrap(webpackPlugin); // 包裹
    
    HappyPack(在项目比较大时效果会比较明显,还可能出现构建速度变慢)

    https://www.npmjs.com/package/happypack

    使用 happypack 提升 Webpack 项目构建速度

    HappyPack 可以让 Webpack 同一时间处理多个任务,发挥多核 CPU 的能力,将任务分解给多个子进程去并发的执行,子进程处理完后,再把结果发送给主进程。通过多进程模型,来加速代码构建。

    const HappyPack = require('happypack');
    const happyThreadPool = HappyPack.ThreadPool({size: 5}); //构建共享进程池,包含5个进程
    mudule.exports = {
      entry:...,
      output:...,
      mudule:{
        rules: [
            {
              test: /\.js$/,
              loader: 'happypack/loader?id=babel', // 开启转换结果缓存
              include: [resolve('src')]
          },
        ]
        },
      plugins: [
         // happypack并行处理
         new HappyPack({
             // 用唯一ID来代表当前HappyPack是用来处理一类特定文件的,与rules中的use对应
             id: 'babel',
             loaders: ['babel-loader?cacheDirectory'],//默认设置loader处理 cacheDirectory对babel-loader进行缓存
             threadPool: happyThreadPool,//使用共享池处理
         })
      ]
    }
    
    HardSourceWebpackPlugin(效果明显,简直就是神器啊)

    HardSourceWebpackPlugin 为模块提供了中间缓存,缓存默认的存放路径是: node_modules/.cache/hard-source
    配置 hard-source-webpack-plugin后,首次构建时间并不会有太大的变化,但是从第二次开始,构建时间大约可以减少 80%左右。

    const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
    module.exports = {
      // ...
      plugins:[
        new HardSourceWebpackPlugin()
      ]
    }
    

    由于这个插件效果太过于明显,太过于强大,以至于使用过后我觉得其他优化都可以忽略不计了。

    缩小构建目标/减少文件检索范围

    缩小构建目标:主要是excludeinclude的使用:(有效果)

    https://www.webpackjs.com/configuration/module/#rule-exclude

    • exclude: 不需要被解析的模块
    • include: 需要被解析的模块
      include和exclude用一个即可,默认exclude的优先级比include高。
    module: {
        rules: [
            ...(config.dev.useEslint ? [createLintingRule()] : []),
            {
                test: /\.vue$/,
                loader: 'vue-loader',
                options: vueLoaderConfig
            },
            {
                test: /\.js$/,
                loader: 'happypack/loader?id=babel', // 开启转换结果缓存
                include: [resolve('src')]
            },
            {
                test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
                loader: 'happypack/loader?id=img',
                options: {
                    limit: 10000,
                    name: utils.assetsPath('img/[name].[hash:7].[ext]')
                },
                include: [resolve('src')]
            },
            {
                test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
                loader: 'url-loader',
                options: {
                    limit: 10000,
                    name: utils.assetsPath('media/[name].[hash:7].[ext]')
                },
                include: [resolve('src')]
            },
            {
                test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
                loader: 'url-loader',
                options: {
                    limit: 10000,
                    name: utils.assetsPath('fonts/[name].[hash:7].[ext]'),
                    // todo-tao 将来删除,调整element-ui@2.8.2字体打包后的引用路径
                    publicPath: '../../'
                },
                include: [resolve('src'), resolve('node_modules/element-ui')]
            }
        ]
    }
    
    减少文件检索范围:这个主要是resolve相关的配置,用来设置模块如何被解析。通过resolve的配置,可以帮助Webpack快速查找依赖,也可以替换对应的依赖。

    https://www.webpackjs.com/configuration/resolve/#resolve-modules

    resolve.modules:告诉 webpack 解析模块时应该搜索的目录resolve.modules用于配置webpack去哪些目录下寻找第三方模块,默认是['node_modules'],但是,它会先去当前目录的./node_modules查找,没有的话再去../node_modules最后到根目录

    所以当安装的第三方模块都放在项目根目录时,就没有必要安默认的一层一层的查找,直接指明存放的绝对位置

    resolve.extensions:文件扩展名 ['js','vue','jsx']尽量少,且将会匹配到的多的文件后缀放到最前面。

    resolve.mainFiles:解析目录时要使用的文件名,默认是index

    resolve: {
      extensions: ['.js', '.vue', '.json'],
      alias: {
          'vue$': 'vue/dist/vue.esm.js',
          '@': resolve('src')
      },
      modules: [path.resolve(__dirname, '../node_modules')]
    }
    

    优化的方式还有很多,比如使用DllPlugin优化,原理是它能把第三方库代码分离开,并且每次文件更改的时候,它只会打包该项目自身的代码。所以打包速度会更快。但是在某些资料中发现这个优化不是很理想,vue和react官方都没有很友好的去用这个优化方式,本次优化项目构建速度目前就用到这些内容,如有更好的方式可以一起交流呀。

    补充:使用HardSourceWebpackPlugin会出现的问题:

    由于HardSourceWebpackPlugin插件做了缓存,所以当更改了webpack配置,或者新增npm包的时候就会出现再次构建新增npm包不生效问题,因为再次构建还是使用的之前的缓存内容新增的npm包内容并没有进行构建,因此在配置发生改变时需要重新构建一次项目,配置如下:

    new HardSourceWebpackPlugin({ // 二次构建缓存优化插件
        environmentHash: {
            root: process.cwd(),
            directories: [],
            files: ['package-lock.json', 'yarn.lock'],
        },
    })
    

    在实例化HardSourceWebpackPlugin插件的时候传入环境hash配置项,官网是这么说的:
    当loaders、plugins、其他构建时的脚本或其他动态依赖项发生更改时,hard-source需要替换缓存以确保输出正确。环境哈希用于确定这一点。如果哈希与以前的构建不同,则将使用新的缓存。

    补充:HardSourceWebpackPlugin 缓存ESlint信息, ESlint错误信息被缓存,二次构建时还会存在

    根据官网提示新增一个插件,用来排除eslint-loader缓存

    new HardSourceWebpackPlugin.ExcludeModulePlugin([
        {
          test: /eslint-loader/
        }
    ])
    

    https://github.com/mzgoddard/hard-source-webpack-plugin

    补充:happy pack使用注意点

    happy pack是用来多进程打包加速,这个多进程指的是采用cpu的进程进行加速打包的。
    因此在使用的时候需要先获取cpu的进程。
    使用os模块 os.cpus().length来获取cpu的进程数。

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

    happy pack原理解析

    补充:弃用happypack使用thread-loader

    在小组分享会上说使用多进程打包加速的时候,有伙伴提出来说,happypack官方停止去维护了,并且webpack4更推荐多进程使用thread-loader.
    安装:
    npm install --save-dev thread-loader
    使用:

    module.exports = {
      module: {
        rules: [
          {
            test: /\.js$/,
            include: path.resolve("src"),
            use: [
              "thread-loader",
              // your expensive loader (e.g babel-loader)
            ]
          }
        ]
      }
    }
    

    https://github.com/webpack-contrib/thread-loader

    相关文章

      网友评论

          本文标题:webpack构建速度优化

          本文链接:https://www.haomeiwen.com/subject/aodpdktx.html