Webpack优化

453 阅读11分钟

Webpack的高级配置,使得代码在编译/运行时性能更好

提升开发体检

SourceMap

为什么? 开发时我们运行的代码是经过webpack编译之后的(), 所有的css和js合并成了一个文件,并且多了很多其他代码,此时如果代码出错那么提示代码出错的位置,我们可能会看不懂,一旦代码量很大,那么我们很难发现错误出现在哪里, 所以我们需要更加准确的错误提示,来帮助我们更好的开发代码

是什么 SourceMap是一个用来生成源代码与构建后代码---映射的文件的方案。 他会生成一个xxx.map文件,里面包含源代码和构建后代码每一行,每一列的映射关系。当构建后代码出错了,会通过xxx.map 文件,从构建后代码出错位置找到映射后源代码出错位置,从而让浏览器提示源代码文件出错位置,帮助我们更快的找出错误根源

怎么用 Dev Tool中可以看出,SourceMap有很多种情况,但是实际开发时,我们只需要关注两种情况即可:

  • 开发模式:cheap-module-source-map
    • 优点:打包编译速度快,只包含行映射
    • 缺点:没有列映射(不过开发代码是有列的,但是生产模式输出后只有一行,所以要关心列)
  • 生产模式:source-map
    • 优点:包含行/列映射
    • 缺点:打包编译速度更慢

提升打包构建速度

HotModuleReplacement

为什么 开发时我们修改了其中一个模块代码,Webpack默认会将所有模块全部重新打包编译,速度很慢,所以我们需要做到修改某个模块代码,就只有这个模块代码需要重新打包编译,其他模块不变,这样打包的速度就会很快 是什么 HotModuleReplacement(HMR/热模块替换):在程序运行中,替换、添加或者删除模块,而无需重新加载整个页面 怎么用 1.基本配置

image.png

OneOf(开发生产模式都可以使用)

为什么 打包的时候每一个文件都会经过所有的laoder处理,会自上而下的找,虽然因为test正则原因实际并没有处理上,但是都要经过一遍,所以会比较慢 是什么 顾名思义就是只能匹配上一个loader,剩下的就不匹配了 怎么用 1.基本配置 image.png

Include/Exclude

为什么 开发的时候我们需要使用第三方库或者插件,所有的文件都下载到node_modules中了,而这些文件是不需要编译就可以直接使用的,所以我们在对js文件处理的时候,就要排出node_modules下面的文件

是什么 include: 包含,只处理xxx文件。 exclude:排除,除了xxx文件之外其他都处理 怎么用 一般样式文件不需要处理,主要开发的时候我们基本不会引入第三方开发的一些样式的,主要自己写样式,如果我们引入了比如bootstrap.css的样式,我们也希望他能以我们的方式运行,所以我们也需要进行处理,所以主要针对js文件处理,也就是babel和eslint

image.png

Cache

为什么 每次打包的时候js文件都要经过eslint和babel编译,速度比较慢,我们可以缓存之前的eslint和babel编译结果,这样第二次打包的时候就会更快 是什么 对eslint检查 和babel编译结果进行缓存

image.png

Thead

为什么 当项目越来越庞大的时候,打包的速度越来越慢,甚至于要好几个小时才能打包出来代码,如果我们想要提升打包速度,主要就是要提升js的打包速度,因为其他的文件比较少,而对js文件处理主要就是eslint,babel,terser(内部用的,只要开启了production模式,webpack内部就会启动terser来压缩代码)三个工具,我们需要提高他们的运行速度,我们可以开启多进程同时处理js文件,这样速度就比之前的单进程打包更快 是什么 多进程打包:开启电脑的多个进程同时打包,速度更快,需要注意,请仅在特别耗时的操作中使用,因为每一个进程启动就大约为600ms左右的开销

怎么用 先判断cpu的核数 我们启动进程的数量就是我们CPU的核数,每个插件可能写的方式不一样,可以查阅对应插件的文档

image.png

image.png

image.png

减少代码体积

Tree Shaking

为什么 开发时我们定义了一些工具函数库,或者引用第三方工具函数库或组件库,如果没有特殊处理的话我们打包的时候会引入整个库,但是实际上可能我们可能只能用上极小部分的功能,这样整个库都打包进来,体积就太大了

是什么 Tree Shaking是一个术语,通常用于描述移除JavaScript中的没有使用上的代码,注意:它依赖 ES Module 怎么用 Webpack已经默认开启了这个功能,无需其他配置

Babel

为什么 Babel为编译的每一个文件都插入了辅助代码,使代码体积过大!Babel对一些公共方法使用了非常小的辅助代码,比如_extend.默认情况下会被添加到每一个需要他的文件中,我们可以将这些辅助代码作为独立模块,来避免重复引入

是什么 @babel/plugin-transform-runtime:仅用了Babel自动对每个文件的runtime注入,而是引入@babel/plugin-transform-runtime并且使用所有辅助代码从这里引用

怎么用 npm i @babel/plugin-transform-runtime -D

image.png

Image Minimizer

为什么 如果开发项目中引入了较多的图片,那么图片体积就会比较大,将来请求速度比较慢,我们可以对图片进行压缩,减少图片体积。注意:如果项目中图片都是在线链接,那么就不需要了,只有本地静态项目才需要进行压缩

是什么 image-minimizer-webpack-plugin:用来压缩图片的插件

怎么用 npm i image-minimizer-webpack-plugin imagemin -D还需要下载剩下的几种,一种是有损压缩 npm i imagemin-gifsicle imagemin-mozjpeg imagemin-pngquant imagemin-svgo -D,另外一种有损压缩 npm i imagemin-gifscle imagemin-jpegtran imagemin-optipng imagemin-svgo -D

webpack.js.org/plugins/ima…

image.png

优化代码运行性能

Code Split

为什么 打包代码时候会将所有的js文件打包到一个文件中,体积太大了,我们如果只要渲染首页,就应该只加载首页的js文件,其他文件不应该加载,所以我们需要将生成的文件代码进行分割,生成多个js文件,渲染哪个页面就加载某一个js的 文件,这样加载的资源就少,速度就更快

是什么 代码分割(Code Split) 主要做了两件事:

  1. 分割文件:将打包生成的文件进行分割,生成多个js文件
  2. 按需加载:需要那个文件就加载那个文件 怎么用 代码分割有不同的实现方式,为了更加方便体现他们之间的差异,我们会分别创建新的文件夹进行演示
  • 多入口

entry是一个对象,输出的名字根据entry起的名字输出,如下,entry 的名字是app,main所以输出对应两个。 image.png

  • 多入口提取公共模块

为什么 多入口的文件,可能调用了公共的代码,我们想要把比较大的公共的代码提取出来

怎么配置, optimization下面的splitChunk打开

image.png

  • 多入口按需加载 比如一些按钮事件

image.png

  • 单入口 开发时候我们可能是单页面应用(SPA),也就是只有一个入口

按需加载 image.png

动态导入 image.png

  • 给提出的模块命名

image.png

chunkname里面的[name]就是webpackChunckName:math image.png 打包输出的名字就是我们自己起的名字 image.png

  • 统一命名

image.png

Preload/Prefetch

为什么 我们前面已经做了代码分割,同时会使用import动态倒入鱼发来进行代码按需加载(我们也叫蓝加载,比如路由懒加载就是这样实现的),但是加载的速度还是不够好,比如:用户点击按钮时才开始加载这个资源,如果资源体积很大,那么用户就会感觉到明显的卡顿效果,我们想要在浏览器空闲时间,加载后续所需要使用的资源。

是什么

  • Preload:告诉浏览器立即加载资源
  • Prefetch:告诉浏览器在空闲时候开始加载资源 共同点:他们都只会加载资源并不会执行,都有缓存 区别:preload的优先级高,prefetch优先级低。Preload只能加载当前页面所使用的资源,prefetch可以加载当前页面资源,也可以加载下一个页面需要使用的资源

他们的问题就是,兼容性比较差。preload,ie完全不兼容,prefetch ie11能兼容但是整体兼容性差 总结:当前页面优先级高的资源用preload加载,下一个页面需要使用的资源用prefetch加载 github.com/GoogleChrom…

Network Cache

当修改其中一个文件,依赖他的文件由于文件名的hash值变了,那么导致引用他的文件也会变。

image.png

Core-js

为什么 我们用babel处理了兼容性问题,并使用了@babel/preset-env只能预设来处理兼容性问题。他能将es6的一些语法进行编译转换,比如尖头函数,点点点运算符等,但是如果是asynnc函数,promise对象,数组的一些方法比如(includes)他没有办法处理,所以此时js仍然存在一些兼容性问题,一旦遇到低版本的浏览器会直接报错,所以我们需要将js兼容性问题彻底解决 是什么 core-js是专门用来做ES6以及以上API的polyfill,polyfill翻译过来就叫做垫片/布丁,就是用社区上提供的一段代码,让我们子在不兼容某些新特性的浏览器上,使用该特效

怎么用

  1. 下载core-js,在main.js中直接引用/按需引用, image.png

  2. 在babel中配置 image.png

PWA

为什么 开发Web App项目,项目一旦处于网络离线情况,就没法访问了,希望能给项目提供离线体验。 是什么 渐进式网络应用程序(progressive web application -PWA)是一种可以提供类似于native app体验的web app的技术,其中最重要的是,在李现时候应用程序能够继续运行功能,内部是通过Service Workers技术实现的 怎么用 webpack.js.org/guides/prog…

  1. npm i workbox-webpack-plugin -D
  2. 配置

image.png 3. main.js注册

image.png

总结

我们从四个角度进行了优化

  1. 提升开发体验 使用Source Map让开发或者上线时候代码报错能有更加准确的错误提示
  2. 提升webpack打包构建速度
    • 使用HotModulereplacement 让开发只重新变易打包更新变化了的代码,不变的代码使用缓存,使的更新速度更快
    • 使用 OneOf让资源文件一旦被某一个loader处理了吗,就不会继续遍历了,打包速度更快了
    • 使用Include/Exclude排除或者只检测某些文件,处理的文件更少,速度更快
    • 使用Cache对eslint和babel处理的结果进行缓存,让第二次打包速度更快
    • 使用thead多进程处理eslint和babel任务,速度更快,注意,要在比较多的时候使用
  3. 减少代码提及
    • 使用Tree Shaking剔除没有使用的多余代码,让代码体积更小
    • 使用@babel/plugin-transform-runtime插件对babel进行处理,让辅助代码从中引用,而不是每个文件都生成辅助代码,使体积更小
    • 使用Image Minimizer对项目中图片进行压缩,体积更小,请求速度更快,如果是在线图片则不需要
  4. 优化代码运行性能
    • 使用 Code Split对代码进行分割成多个js文件,是的单个文件的体积更小,能够并行加载js速度更快,并且通过import动态导入语法进行按需加载,从而达到需要使用时才加载该资源,不用时不需要加载
    • 使用Preload/prefetch对代码进行提前加载,等未来使用时就能直接使用,用户体验更好
    • 使用Core-js对js进行兼容性处理,让我们能运行在低版本浏览器
    • 使用PWA能让代码离线访问,从而提升用户体验