提升webpack打包速度的方法

当我们去打包一个大型项目的时候,有的时候完成打包需要非常多的时间,遇到这种问题的时候,我们就要考虑用一些方法来提升webpack的打包效率

如何提升webpack打包速度呢?

一. 跟上技术的迭代,尽可能使用新版本的webpack,node,npm, yarn
  • 如果我们想提升webpack的打包速度,我们可以升级webpack的版本,或者升级我们的node版本,npm包管理器的版本,或者yarn管理工具的版本;

  • 为什么升级这些工具,可以提高webpack的打包速度呢?

  • 主要是因为webpack在做版本更新的时候,内部肯定会做一些优化,所以如果我们更新了webpack的版本,webpack的打包速度肯定会有所提升;

  • 而webpack又是建立在Node这样一个JS运行环境之上的,如果node进行了升级,那就意味着node的运行效率会有提升。webpack运行在node之上,很显然它的速度也会有所提升;

  • 当我们安装了新版本的npm ,yarn这类的管理工具之后,模块之间如果遇到一些相互引用的情况下,新的包管理工具,可以更快的帮我们分析一些包依赖,或者做一些包引入,这也能间接提升webpack的打包速度

二. 在尽可能少的模块上应用loader来提升webpack的打包速度
  • 比如,我们在打包js结尾的文件时,配置的exclude,如果我们不配置exclude: /node_modules/,然后我们在代码里引入了 node_modules里的第三方模块的js文件的时候,当我们运行打包命令的时候,这个第三方模块的JS文件会被打包编译,但其实这是没有必要的,因为一般情况下,第三方模块已经是被打包编译过了的,所以额外进行一次编译的话,就会降低打包的速度
    image.png
  • 当然了,出了使用exclude,我们还可以用include来达到类似的效果;exclude: /node_modules/的意思是,不对node_modules目录下的JS文件进行编译;而include:path.resolve(__dirname,'../src')这样配置的意思是,当我遇到一个JS文件或者JS模块的时候,只有它在src源代码文件夹下,才会去使用babel-loader来帮我们做语法的转换;如果引入了其他目录下的JS文件,就不会走这个loader了
  • 合理的使用include或者exclude可以降低loader的使用频率,提高打包速度
  • 那么图片文件是否需要配置include或者exclude呢,实际上是不需要的,因为无论引入哪里的图片,实际上都需要url-loader来帮我们打包到dist目录下
三. Plugin尽可能精简,并确保可靠
  • 打包生成线上代码的时候,为了让线上代码尽可能的小,让用户加载速度尽可能的快,所以我们需要对代码做压缩,之前的配置中,我们在线上环境的配置文件中使用了optimize-css-assets-webpack-plugin这样一个插件,对我们的CSS文件进行了压缩,可是如果我们在开发环境下,是没有必要对代码进行压缩的。因为开- 发环境下,是我自己用,不需要考虑加载速度的问题。
  • 由此,我们可以知道,如果不对代码进行压缩,那就解决了压缩代码的时间,我们的打包速度就会更快一些,所以在做webpack打包的时候, 没有必要使用的插件就尽量不用,它会降低我们的打包速度。
  • 同时,我们在使用插件的时候,尽量使用webpack官网方式推荐使用的插件,因为这些插件它的性能经过了官网的测试,是比较快的;而一些第三方公司或个人的插件,也会可以帮我们解决一些问题,但是它的性能得不到保证,使用它们的话,很有可能会降低我们的打包速度,所以我们使用插件的时候,最好是社区里验证过的性能比较好的插件
四. resolve参数合理配置
  • 通过合理的优化 resolve配置项,我们可以让webpack打包速度更快;
    当我们在代码里引入一个模块的时候,而这个模块的后缀,并不是我们常见的js结尾,而是jsx的文件。然后我们还像平时那样引入,不带后缀;会报错,说找不到这个模块。

这种情况,我们可以配置一个resolve下的extensions

image.png

方便我们引入(不写后缀的引入)这种逻辑性的文件,当然如果在里边配置一些像.jpg .png .css之类的也不是不可以,只是会对性能有所影响,建议还是只配置js或者jsx这类逻辑性的文件后缀

  • 如果我们引入child这个组件的时候,写的更简洁一点,比如这样import Child from './child/'; 我们的想法是,自动帮我们识别出child目录里的child组件;如果直接import Child from './child/'这样写,可以吗? 很明显不行;如果我们把child.jsx改为index.jsx就好用了,这是因为,当我们去引入一个目录下东西的时候,默认会帮我们引入名字叫做index这一类的文件
    image.png
  • 我们可以通过resolve配置来实现这个想法;当我们配置了mainFiles之后,当我们再去引入一个目录下的东西的时候,会先帮我们找目录下index名字的文件,如果不存在,就找名字叫做child的文件
    image.png

我们在每次修改了webpack配置后重启服务器,webpack都会帮我们打开一个新窗口展示,如果不想每次都开新窗口,可以把devServer 里的 open:true关闭掉就可以了

  • 虽然mainFiles配置解决了我们的问题,但是它也会带来性能上的问题,因为需要在路径下不停的去找我们配置的名字的文件是否匹配,所以一般情况下我们不会配置这个参数
  • 如果我们想这样引入 import Child from 'lee';正常情况下,是会报错的,因为我们lee不是一个第三方模块,而且我们也没有安装这样的模块,这样写法跟我们引入第三方模块是一样的,比如我们引入react;但是我们可以配置alias参数
    image.png
  • 这样配置的意思是,当我们在引入的时候,看到的是lee这个路径的时候,实际上它是path.resolve(__dirname, '../src/child')这个路径的别名
    image.png
  • 所以,通过这个例子我们应该明白,alias参数是我们可以经常用到的一个配置;它的使用场景如下,假如有这样一个路径src/a/b/c/child.js,正常情况下我们要引入child.js需要这样写 , import Child from './src/a/b/c/child'这样非常麻烦,通过刚才的alias我们就可以这样配置child:path.resolve(__dirname,'../src/a/b/c/child')
五. 使用DLLPlugin提升打包速度
  • 我们在引入第三方模块的时候,每次重新打包的时候,webpack都要重新分析这些第三方模块,然后把它们打包到我们的项目之中。但是我们知道,一般情况下,这些第三方模块的代码是轻易不会变的


    image.png
  • 那我们怎么针对这个问题进行优化呢,我们可以把这些第三方模块单独打包生成一个文件,只在第一次打包的时候去分析这个文件里的代码,之后再打包的时候,直接用这个分析过的结果就可以了,这是一个最理想的优化方式。具体操作步骤如下

  • 再创建一个配置文件,姑且命名为webpack.dll.js

    image.png

  • 然后再在package.json里再配置一个命令

    image.png

  • 然后我们运行打包命令npm run build:dll,因为我们没有配置mode属性,所以虽然打包成功了,还是给了我们警告

    image.png

  • 配置之后,我们再打包警告没有了


    image.png
  • 这个时候,我们已经把引入的三个模块,打包到了dll文件夹下的vendors.dll.js文件里

    image.png

  • 但是,虽然我们把三个模块打包到了一个单独的文件里,我们还没有使用这个文件,我们要怎么使用这个文件,才能提升webpack的打包效率呢?我们可以把这个文件里的内容通过一个全局变量暴露出来,具体配置如下

    image.png

  • 现在我们有了这个全局变量,但是我们的index.html里,并没有引入vendors.dll.js文件,所以我们在控制台去打印vendors这个全局变量,是找不到的

    image.png

  • 现在,只是引入了这3个JS文件,并没有引入vendors.dll.js文件

    image.png

  • 那我们怎么引入呢? 我们需要安装一个插件 npm install add-asset-html-webpack-plugin --save,这个插件的作用就是往html-webpack-plugin上再去增加一些静态的资源,安装之后,我们要先引入

    image.png

  • 引入之后,我们去调用


    image.png
  • 配置好以后,我们重新启动一下服务器npm run dev,我们可以看到,页面已经引入了我们生成的vendors.dll.js文件,页面上控制台也可以访问到vendors这个全局变量了

    image.png

    image.png

  • 到了这一步,我们实现了 第三方模块打包一次,但是我们并没有实现第一次打包,后边就用打包的成果,因为我们现在无论是在webpack.common.js里,还是webpack.dev.jswebpack.prod.js里,我们打包的过程中,都是直接引入的第三方模块,而不是用我们生成的全局变量vendors,要知道现在vendors已经包含了我们要使用的三个模块,当我们运行src下的index.js的时候,里边的模块引入还是使用的node_modules下的第三方模块,

    image.png

  • 所以我们引入第三方模块的时候,要使用vendors.dll.js文件引入,这个时候就需要用到DLLPlugin;这个插件的时候,需要引入webpack

    image.png

  • 配置好以后,我们再运行打包命令npm run build:dll,可以看到在dll文件夹下,多了一个叫做vendors.manifest.json的映射文件

  • 有了这个映射文件,在webpack做打包的时候,就可以结合生成的vendors.dll.js文件和这个映射文件,对我们源代码进行分析,一旦分析出来,使用的模块内容,是在vendors.dll.js里,那么它就会直接去使用vendors.dll.js里的内容了,就不会去node_modules引入第三方模块了

  • 那怎么结合全局变量和刚才生成的vendors.manifest.json文件,进行我们的webpack的配置呢?需要再借助一个插件DllReferencePlugin

    image.png

  • 经过上边的配置,我们已经实现了我们想要的功能,我们再去打包会发现,打包时间是554ms

    image.png

  • 假如我们现在把插件注释掉,这样每次打包的时候,就会直接到node_modules引入模块,这个时候打包的时间会大于刚才,为718ms

    image.png

    image.png

  • 现在我们把reat,react-dom,lodash这三个第三方模块打包到一个文件了,假如我们想拆分一下,比如把reat,react-dom打包到一起,lodash单独放到vendors.dll.js文件里;那么我们需要这样去配置

    image.png

  • 然后还需要通过AddAssetHtmlWebpackPlugin把react.dll.js文件也放到html-webpack-plugin生成的index.html,这样全局变量里才会多出一个react; 也需要通过DllReferencePlugin插件,去分析react.manifest.json映射文件

    image.png

  • 在引入第三放模块比较少的情况下,我们可以像上边那样去配置,可以如果我们引入很多的第三方模块,而且我们希望把它们都单独打包到一个文件里,拿我们岂不是要配置很多的 AddAssetHtmlWebpackPluginDllReferencePlugin了,这样太麻烦了。如果我们可以去遍历dll文件夹下有多少的文件,然后把它们都自动添加到plugins里就好了

  • 我们可以这样配置,首先我们把通用的插件拿出来放到一个数组里


    image.png
  • 然后我们引入node的核心模块fs(在 NodeJS 中,所有与文件操作都是通过 fs 核心模块来实现的,包括文件目录的创建、删除、查询以及文件的读取和写入),然后通过fs调用readdirSync方法去读dll文件夹里的内容
    然后打印一下读取的内容,我们先把plugins配置里的内容注释掉先,然后运行npm run build

    image.png

    image.png

    image.png

  • 我们成功读取了dll文件里的所有文件,然后我们循环files这个数组,把数组里的值取出根据情况放入到plugins

    image.png

  • 然后把生成的plugins数组,配置到plugins配置项就可以了


    image.png
  • 然后我们在运行npm run dev,发现是正常运行的

    image.png

  • 我们测试下,如果我们再加一个jquery的话(jquery要先安装),会正常运行吗?

    image.png

  • 我们再运行一下npm run build:dll

    image.png

  • 然后再运行npm run dev,发现成功了

    image.png

  • 所以,经过上边配置,无论我们引入了多少第三方模块,要把这些模块拆分成几个文件打包,都可以通过entry配置去修改就可以了,剩下的工作webpack会按照我们配置自动帮我们完成

六.控制包文件的大小
  • 我们在做项目打包的时候,我们应该让打包生成文件尽可能的小,我们在写代码的时候,经常会在页面里引入一些没用的模块,但是我们并没用使用这些模块,而我们也没配置Tree Shaking,这就导致代码里有很多冗余的代码,这些冗余代码就会拖累webpack的打包速度。
  • 在我们的源代码里,一些用不到的包,要通过Tree Shaking去除掉,或者直接不引入它,这样去控制webpack打包生成文件的大小,从而提升打包的速度
  • 我们还可以通过splitchunksplugin这样的插件对代码进行拆分,把一个大的文件拆分成小的文件,进行webpack的打包处理,这样也可以提升webpack的打包速度
七. thread-loader,parallel-webpack,happypack 多进程进行打包
  • webpack默认是同构NodeJS来运行的,所以是一个单进程的打包过程,有时候,我们可以借助node里的多进程来帮助我们提升webpack的打包速度
八. 合理使用 sourceMap
  • 打包的时候,生成的sourceMap越详细,打包的速度就越慢,所以我们要思考,不同环境打包的时候,什么样的sourceMap是最合适的,既要保证我们及时发现代码里的问题。又可以尽可能的提升我们的打包速度
九. 结合stats 分析打包结果

也就是我们可以通过命令,来生成一个描述这次打包情况的Stats.json文件,通过借助一些线上或者本地的打包分析工具,我们可以分析这些打包过程中的一些情况,比如哪个模块打包时间比较长,哪个模块打包时间比较短,从而分析出哪些部分打包耗时比较多,从而做针对性的优化以提升打包速度

十.开发环境内存编译
  • 我们在开发环境的时候使用的是webpack dev server,它在做打包的时候,不会生成dist目录,他会把编译生成的内容放到内存里,内存的读取,肯定要比硬盘的读取快的多,所以通过这种手段,也可以让我们在开发的过程中,webpack的性能得到很大的提升
十一. 开发环境,无用插件剔除
  • 比如项目调试的时候,我们并不需要对代码进行压缩,我们就应该把mode设置成development而不是production,如果开发环境下就压缩的话是没用意义的,这会降低webpack的打包速度
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354

推荐阅读更多精彩内容