使用 HappyPack 和 DllPlugin 来提升你的 Webpack 构建速度

@(Blogs)[webpack, Front-End]

本文原文发表在:medium.com/@Erichain/%…
本文采用的 Webpack 版本为 2.0+
本文源代码地址:github.com/Erichain/we…

如果你问我对 Webpack 什么印象的话,我只能告诉你,慢,真的慢。即使他的配置如文档所说(当然,它的文档也不是那么好)很简单,不像 Grunt 或者 Gulp 那样需要一堆配置,只需那么几十行就能够配置一个构建系统,我依然觉得,这个构建工具很慢。或许,是从它的文档开始,我就印象不好了?OK,这个话题到此为止,我们开始我们的正题吧。

本篇文章面向的不是 Webpack 新手,如果你对 Webpack 还不太熟悉的话,建议去阅读它的官方文档。当然,我们肯定也会涉及到一些基础的东西。

本文重点讲解对生产环境的构建的性能的提升,如果需要对本地构建的性能进行提升的话,可以在本文结束之后,自己寻找一下解决方案哦。当然,还有一点需要说明的是,本文中的代码在本地不一定真正能够在浏览器中运行,有需要的可以自行搭建本地的构建系统。


一点基础

使用过 Webpack 的朋友肯定知道,Webpack 的最简单的配置如下:

module.exports = {entry: {app: './src/app.js'},output: {path: path.join(__dirname, 'dist-[hash]'),filename: '[name].[hash].js'}
};复制代码

这样的配置会将我们的文件打包成为一个 app.[hash].js 文件。这样针对的一般是我们的项目不算大的情况,并且公用模块比较少的情况(当然,公用模块较多的话,配置肯定也不会这么简单了)。

对于项目中有用到预处理器,ES2015+ 或者其余的需要编译后在浏览器上运行的语言,我们需要做的就是为这些东西添加上对应的 loader,然后,Webpack 就会自动的帮我们进行处理了(老实说,这一步还是挺方便的)。

一些 loader 配置示例如下:

rules: [{test: /\.jsx?$/,loader: ['babel-loader?presets[]=react,presets[]=latest&compact=false'],}, {test: /\.scss$/,loader: ['style-loader','css-loader','postcss-loader','sass-loader'],}, {test: /\.jpe?g|png|svg|gif/,loader: ['url-loader?limit=8192&name=assets/images/[name]-[hash].[ext]'],}
]复制代码

另外,我们还可以通过一些插件来更多的定义 Webpack 的打包行为。比如,如果我们有很多第三方库的引用,并且,多个地方都会引用到这些库,我们就可以使用 Webpack 的 CommonsChunkPlugin 来将这些公用的代码打包成一个文件(当然,至于速度嘛,我们后面再说),然后,将我们页面的业务代码打包成为一个文件。

Webpack 的主要配置就这几项,其他更多的更深入的配置可以查看 Webpack 的官方文档。

速度慢

尽管 Webpack 配置起来很方便,但是,按照一般的配置来的话,构建的速度真的是太慢了,每构建一次都会花掉相当长的时间,这对于开发者们来说简直是噩梦。

可是,速度为什么会这么慢呢?

以我所在的项目为例,由于我们的项目存在多个 entries(大概四十多个),所以,我们的 Webpack 采用的配置是将公用的第三方库通过 CommonsChunkPlugin 来打包成为一个 common.js

根据这个 common.js 的内容来看,这里面存放的就是各个 entry 引用的公有的代码,比如,我们的很多组件都会用到 React 或者 Redux 这些第三方库。通过将公有的代码单独打包成一个文件,然后再将业务代码打包成一个文件,这样一来,业务代码模块本身的体积就会减小很多,页面的加载速度也能够得到很大的提升。

虽然这样打包的方式能够在一定程度上提升页面的加载速度,但是,我们简单的想一想也知道,CommonsChunkPlugin 会去将所有 entry 中的公有模块遍历出来再进行编译压缩混淆,这个过程是非常缓慢的(我们的项目以前在使用这种方式的时候,在这一步会花上至少十二分钟的时间,你可以想象这个过程有多么漫长)。

经过了几个迭代的痛苦的打包上线的过程之后,我们终于不能忍了,决定对这个构建系统进行改造。

改造的过程

说实话,一开始我其实是没有任何头绪的,我只知道这个构建的过程慢,但是,并不清楚应该从何处开始进行改造。

与同事们进行了一些商讨之后,我准备从以下几个方面入手:

  • 减少构建的文件,减小文件大小:我们的项目中存在太多的无用的文件和代码,我决定先删除这些无用的东西
  • 移除 CommonsChunkPlugin
  • Search with Google

第一步的作用其实并不明显,我删除了很大一部分的无用的图片和代码,但是,构建速度并没有明显的提升。

第二步,简单的移除掉 CommonsChunkPlugin 的话,构建速度确实会快很多,但是,这样打包出来的项目就不能够运行了,所以,还需要结合第三步(必须要感谢这个世界存在 Google)。

我在网上找到了许多相关的问题,关键性的建议有以下几个:

  • css-loader 的版本回溯到 0.15 及其以前的版本
  • 使用 HappyPack
  • 使用 DllPlugin

首先,第一点,降低 css-loader 的版本。

在 GitHub 上有这样一个 issue:0.15.0+ makes Webpack load slowly。按照 issue 中大家的讨论,我将我们项目中的 css-loader 的版本降到了 0.14.5。满怀期待的以为这样就能够提升一部分速度,但是,结果是令人失望的——构建的速度并没有明显的改变。我试着构建了好几遍,速度依然没有提升,所以,第一个方法失败,我将 css-loader 的版本恢复了回来。

那么,继续尝试第二个方法,也是本文将要重点说明的方法之一,那就是使用 HappyPack。

使用 HappyPack

HappyPack 允许 Webpack 使用 Node 多线程进行构建来提升构建的速度。

使用的方法与在 Webpack 中定义 loader 的方法类似,只是说,我们把构建需要的 loader 放到了 HappyPack 中,让 HappyPack 来为我们进行相应的操作,我们只需要在 Webpack 的配置中引入 HappyPack 的 loader 的配置就好了。

比如,我们编译 .jsx 文件的 loader 就可以这样写:

new HappyPack({id: 'jsx',threads: 4,loaders: ['babel-loader?presets[]=react,presets[]=latest&compact=false'],
})复制代码

其中,threads 指明 HappyPack 使用多少子进程来进行编译,一般设置为 4 为最佳。

编译 .scss 文件的 loader 这样写:

new HappyPack({id: 'scss',threads: 4,loaders: ['style-loader','css-loader','postcss-loader','sass-loader',],
})复制代码

其中,需要注意的一点就是,在使用 HappyPack 的情况下,我们需要单独创建一个 postcss.config.js 文件,不然,在编译的时候,就会报错。

由于 HappyPack 对 url-loaderfile-loader 的支持度的问题,所以,我们此处,打包图片文件的时候,并没有使用 HappyPack。

postcss.config.js 的配置就像下面这样(根据你的需求,定制你自己的配置):

module.exports = {autoprefixer: {browsers: ['last 3 versions'],}
};复制代码

定义好了我们 HappyPack 的 loader 之后,我们直接在我们的 Webpack 的配置的 plugins 一项中,引入就好了。

那么,我们在编译的时候,就会看到下面的输出:

@HappyPack 输出|center

这就是 HappyPack 在编译的时候的输出内容。

但是,我们的关注点不是它输出了什么,而是说,我们的构建速度有没有提升。

当然,结果是令人失望的,我们单独使用 HappyPack 的情况下,构建速度并没有明显的提升(当然,或许有所提升但是我没有发现也有可能)。

所以,为了进一步的提升我们的构建速度,我们将采取第三种方案,那就是 DllPlugin。

使用 DllPlugin

仔细阅读过 Webpack 文档的朋友肯定对这个插件会有印象,或者说知道这个插件是干嘛用的。其实,我们此处也是基于 Webpack 的文档的一些说明,然后,结合我在项目中的实践来为大家讲解这个插件。

在 Webpack 中,DllPlugin 并不是单独的使用的,而是需要与一个名为 DllReferencePlugin 的插件结合起来使用的。

熟悉 Windows 的朋友就应该知道,DLL 所代表的含义。在 Windows 中,有大量的 .dll 文件,称为动态链接库。

在 MSDN 上,微软是这样解释动态链接库的:

A dynamic-link library (DLL) is a module that contains functions and data that can be used by another module (application or DLL).

大概的意思就是说,动态链接库包含的是,可以在其他模块中进行调用的函数和数据。

文档里面还有一句话是这样说的:

DLLs provide a way to modularize applications so that their functionality can be updated and reused more easily.

动态链接库提供了将应用模块化的方式,应用的功能可以在此基础上更容易被复用。

回到我们的项目中,类似的,我们其实要做的也是将各个模块中公用的部分给打包成为一个公用的模块。这个模块就包含了我们的其他模块中需要的函数和数据(比如,其他组件所需的 React 库)。

使用 DllPlugin 的时候,会生成一个 manifest.json 这个文件,所存储的就是各个模块和所需公用模块的对应关系。

说了这么多,我们不如直接来看看这个插件到底是怎么使用的:

首先,我们需要一个文件,这个文件包含所有的第三方或者公用的模块和库,我们在此将其命名为 vendor.js,文件的内容如下:

import 'react';
import 'react-dom';复制代码

由于我们的示例项目中只用到了这两个公用的第三方库,所以,我们此处只需要引入这两个库就行了。

在打包的时候,我们将这些公用的模块单独打包成一个文件,然后,通过生成的 manifest.json 文件对应过去。所以,我们需要单独创建一个 webpack.config.vendor.js

文件内容其实很简单:

const webpack = require('webpack');
const path = require('path');module.exports = {entry: {vendor: [path.join(__dirname, 'src', 'vendor.js')],},output: {path: path.join(__dirname, 'dist-[hash]'),filename: '[name].js',library: '[name]',},plugins: [new webpack.DllPlugin({path: path.join(__dirname, 'dll', '[name]-manifest.json'),filename: '[name].js',name: '[name]',}),]
};复制代码

可以看到,我们主要的操作是在 plugins 配置中,生成的文件名就是我们所定义的 entry 的名称,JSON 文件名可以根据自己的需要来命名。像上面这样,我们就可以将我们的一些公用模块打包出来了。

运行以下命令:

webpack -p --progress --config webpack.config.vendor.js复制代码

我们就可以看到这样的输出:

@DllPlugin 打包输出|center

这样,我们就完成了构建的第一步。下一步,我们需要在构建应用的配置文件中,加入我们的 DllPlugin 的配置。

这时候,我们就需要用到 DllReferencePlugin 了。

在我们的主要配置文件中,加入以下的配置:

const manifest = require('./dll/vendor-manifest.json');// ... 其他完美的配置plugins: [new webpack.DllReferencePlugin({manifest,}),
],复制代码

就这样,我们的所有工作就完成了,我们只需要运行一条命令,就能够看到构建速度的巨大提升。

当然,为了更完美,我们可以将 DllPlugin 和 HappyPack 结合起来使用,效果会更好。具体的代码细节,此处不予展示,朋友们可以直接去 GitHub 上查看。

为了方便构建,我们可以写一个脚本将构建过程简单化。在我的 GitHub 项目里面有相关的脚本,包含了一些基础的操作,有需要的朋友可以去查看。此处,我们就认为我们的命令可以直接构建了。

为了体现出构建速度的区别,我们先运行 npm run build,这是采用普通方式进行构建的命令。

@采用普通构建方式的构建时间|center

可以看到,构建时间为 20353ms,换算下来为 20s 左右。

接下来,我们运行 npm run build dll,通过 DllPlugin 和 HappyPack 进行构建。

@构建 vendor.js 文件的时间|center
@构建 app.js 文件的时间|center

我们将两个时间加起来,总共为 12184ms,换算下来为 12s 左右。快了将近一倍的时间!这还只是文件少的情况。在我们的实际项目中,构建时间提升了 3 倍多,所以,可以看到 DllPlugin 的强大之处。

一点总结

本文只是寻找了这样几种能够提升构建速度的解决方案,我相信,方法肯定不止这些,一定还有更多的解决方案等待我们去发现。所以,希望各位朋友能够对本文中不足的地方提出建议,希望与大家共同学习,共同进步。


References

OPTIMIZING WEBPACK FOR FASTER REACT BUILDS

Optimizing Webpack build times and improving caching with DLL bundles

Dynamic-Link Libraries

使用 HappyPack 和 DllPlugin 来提升你的 Webpack 构建速度相关推荐

  1. android studio导出apk_Android 应用构建速度提升的十个小技巧

    应用的构建速度会直接影响开发效率,本文将带您通过改造一个 Android 应用: "Google 追踪圣诞老人 (Google Santa Tracker)" 来为大家提供十个小技 ...

  2. cli dll打包 vue_vue-cli3使用 DllPlugin 实现预编译提升构建速度

    vue-cli3使用 DllPlugin 实现预编译提升构建速度 发布时间:2020-08-29 11:24:27 来源:脚本之家 阅读:93 在项目打包上有两个目标:减少打包代码体积和加快打包速度 ...

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

    本文简单介绍了 Happypack 的简单使用,不了解的同学可以进来看一看.也许会有所帮助. Happypack 作用 在使用 Webpack 对项目进行构建时,会对大量文件进行解析和处理.当文件数量 ...

  4. happypack提升项目构建速度

    happypack提升项目构建速度 前言 最近在看<深入浅出webpack>看到了happypack.就想起公司一vue项目,每次项目启动都将近2分钟.等的实在让人不耐烦,都够我支付宝偷一 ...

  5. 1. webpack打包速度的优化----happyPack

    webpack打包优化的目的 (1). 使打包的体积更小: (2). 使打包的速度更快: 1. 如何使打包的速度更快: (1). 用happypack提升项目的构建速度:happypack只作用在lo ...

  6. webpack优化篇(四十六):充分利用缓存提升二次构建速度

    说明 玩转 webpack 学习笔记 缓存 目的:提升二次构建速度 缓存思路: babel-loader 开启缓存 terser-webpack-plugin 开启缓存 使用 cache-loader ...

  7. Next.js 7发布,构建速度提升40%

    Next.js团队发布了其开源React框架的7版本.该版本的Next.js主要是改善整体的开发体验,包括启动速度提升57%.开发时的构建速度提升40%.改进错误报告和WebAssembly支持. \ ...

  8. 一直在构建版本_教你如提升Gradle90%的构建速度

    一.第一个最有效的办法 检查你使用的Gradle插件版本,是不是最新的.如果不是最新的,请升级到Gradle插件的最新版本. Gradle在升级过程中一直对构建速度做了优化,升级到Gradle最新版本 ...

  9. 新技能Get:如何利用HTTP技术提升网页的加载速度

    在这个信息爆炸的时代,使用移动终端获取新鲜信息已经是大势所趋,但是移动网页浏览速度还有巨大的提升空间.据 Strangeloop Networks 统计,在同样的网络条件下,使用移动端访问相同网页平均 ...

最新文章

  1. opencv精要(3)-win下codelite的opencv配置
  2. 16进制颜色透明_PPT | 如何快速提取图片颜色之人生太卷
  3. linux写一个脚本杀进程,linux下如何自动检测并重新启动一个死掉的进程,然后再把它杀死:)(shell脚本实现)...
  4. 支付宝双接口ILLEGAL_EXTERFACE错误问题
  5. 遵循Java EE标准体系的开源GIS服务平台之三:数据发布与访问
  6. [开发笔记]-使用jquery获取url及url参数的方法
  7. HTML5和CSS3系列(四):常见样式、背景、Web字体、2D转换、过渡
  8. NLP实战之textRNN中文文本分类
  9. dream_c梦想标准化语言评估,孩子语言发展落后,诊断治疗需“量体裁衣”
  10. springMVC中ajax使用json 对象 出现 状态代码: 415 Unsupported Media Type
  11. wbin笔记本商务版博通机型装(原版黑苹果)单MacOS流程记录(备忘)
  12. AI初学者必看的4个顶级人工智能领域岗位
  13. 计算机指令exec,2.1.3 使用jexec工具管理jail虚拟机
  14. 英语语法2-一般过去时
  15. android手机配什么蓝牙耳机,安卓手机配什么蓝牙耳机好?安卓系统蓝牙耳机推荐!...
  16. 数据库可视化工具 — DBeaver,DataGrip,Navicat,pl/sql Developer
  17. 图解硬盘低格操作方法
  18. 2022年化工自动化控制仪表考试题及在线模拟考试
  19. SMMU架构手册之数据结构和转换流程(2)
  20. html5 设置设备震动,HTML5+ - 拨打电话、保持屏幕唤醒、设备震动

热门文章

  1. 上海交大VS中科大计算机,中国科学技术大学和上海交通大学哪个好?3个理由可直接做出决定...
  2. 如果你月薪不到6千,请收好这7个app,能让你自我提升,工资翻倍
  3. A-level 音乐科目值得去学习吗?
  4. EN 1096-4: 建筑玻璃.涂层玻璃.第4部分:产品标准
  5. 判断一个单词拼写是否正确
  6. AndroidStudio突然出现大面积乱码
  7. JavaScript-流程控制语句详解
  8. JavaScript中的9种控制流语句
  9. Mina快速编码测试验证示例
  10. 《大数据分析原理与实践》一一1.5 全书概览