webpack构建速度优化

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 将来删除,调整[email protected]字体打包后的引用路径
                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构建速度优化)