Web 前端性能优化之五:构建优化

news2024/11/20 1:42:41

4、构建优化

资源的合并与压缩所涉及的优化点包括两方面:一方面是减少HTTP的请求数量,另一方面是减少HTTP请求资源的大小。

1、HTML 压缩
1、什么是 HTML 压缩

在这里插入图片描述

百度首页部分 HTML 源代码

在这里插入图片描述

谷歌首页部分 HTML 源代码

虽然这些格式化的字符能带来很好的代码可读性,但对浏览器解析过程来说其实并不需要,反而还增加了资源的开销。

HTML压缩就是要删除在文本文件中有意义的,但在HTML中并不参与解析的字符。这些字符包括空格、制表符、换行符及一些其他意义的字符,如HTML注释等。

如果单纯只看一个文件的压缩,可能效果并不明显,比如就以前面百度首页为例,原始字符长度为158519,经过压缩之后的长度为157334,长度减少了约0.75%。

难道这就能说明HTML压缩的效果不明显吗?其实我们不能这么考虑,对大型的互联网公司来说,每一个请求的减少都是一个非常大的优化。以谷歌为例,它的网络流量占到了全网流量的40%左右,假如网络流量能达到5ZB(1ZB=109TB),以谷歌的流量占比,它当年的实际网络流量就是5ZB×40%=2ZB,那么当谷歌经过优化让每1MB的请求减少一个字节,则整年便可节省2000TB的流量。如果以每GB流量一毛钱计算,那么一年省下来的开支也不是个小数目,这就是要进行HTML压缩的原因。

2、如何压缩
1、使用 html-minifier

使用nodejs所提供的html-minifier工具进行压缩,它涉及很多参数的配置,包括是否去掉注释removeComments,是否去掉空格collapseWhitespace,是否压缩HTML中的JavaScript的minifyJS及是否压缩HTML中的CSS的minifyCSS,具体方式如下:

在这里插入图片描述

2、CSS 压缩

CSS代码也能进行压缩,而且很有必要去压缩,如下是谷歌首页经过压缩的部分CSS源代码:

在这里插入图片描述

谷歌首页部分 CSS 源代码

从中非常直观的感受是它去掉了回车和换行,此外这里的CSS压缩还做了两件事:首先是无效代码的删除,因为对有些CSS来说,无效的代码可能是注释和无效字符,需要将这些无效的代码删除,这一步很重要;其次是CSS语义合并,通常我们写的CSS可能由于文件层级的嵌套,很难避免一定的语义重复,所以就需要进行语义合并。

可以使用html-minifier针对HTML中的CSS进行压缩。除此之外,还可以使用clean-css进行CSS的压缩,其基本使用方法如下:

在这里插入图片描述

clean-css也有许多配置项,比如是否基于语义进行合并的merging,是否优化颜色取值的colors,以及诸多对不同类型浏览器兼容性的规则导出。

3、JavaScript 压缩与混淆

JavaScript部分的处理主要包括三个方面:无效字符和注释的删除、代码语义缩减和优化及代码混淆保护。无效字符和注释的删除原理与HTML和CSS的压缩类似。

1、代码语义缩减和优化

通过对JavaScript的压缩可以将一些变量的长度进行缩短,比如说原本一个很长的变量名经过压缩后,可以用很短的像a、b来代替,这样能进一步有效地缩减JavaScript的代码量。同样还可以针对一些重复代码进行优化,比如去除重复的变量赋值,将一些无效的代码进行缩减与合并的优化。

在这里插入图片描述

2、代码混淆保护

由于任何能够访问到网站页面的用户,都可以通过浏览器的开发者工具查看到前端的JavaScript代码,如果前端代码的语义非常明显,没进行压缩也没进行混淆,其格式还完整保留,那么理论上任何访问网站的人都可以轻易地窥探到我们代码中的逻辑,从而去做一些威胁系统安全的事情。所以进行JavaScript代码压缩和混淆的处理,也是对我们前端代码的一种保护。

如何来防止别有用心之人窥探到我们业务的核心代码呢?实际上就是进行JavaScript压缩,将前端源代码的可读性变得尽可能低,混淆变量与方法的命名。

对大部分公司来说,HTML压缩可有可无,但是CSS与JavaScript压缩却是必须要进行的。

JavaScript压缩处理也有类似的第三方库可供使用:uglifyJS2。可以通过npm引入uglify-js库来使用,也可以结合构建工具一起使用,其单独使用时的方法如下:

在这里插入图片描述

其中压缩混淆的配置对象结构如下:

在这里插入图片描述

4、webpack 构建与优化
1、尽量与时俱进:尽可能使用最新稳定版本的webpack、nodejs、npm或yarn能有效地提升打包构建的效率。
2、较少 Loader 的执行

根据具体情况使用include或exclude,在尽可能少的模块上执行Loader。webpack的配置文件如下:

在这里插入图片描述

这里关注module字段中对JavaScript文件的处理规则,如果不加exclude字段,则webpack会对该配置文件所在路径下的所有JavaScript文件使用babel-loader,虽然babel-loader的功能强大,但它执行起来很慢。

这样所处理的JavaScript不仅会包含我们的项目源代码,还会涉及node_modules路径下项目引用的所有第三方文件。由于第三方库的文件在发布前本身已经执行过一次babel-loader,没必要再重复执行一次,增加不必要的打包构建耗时,所以exclude字段不可省略。

同时与之对应的还有一个include字段,其使用含义与exclude相反,即仅对其指定范围内的JavaScript文件进行处理,以降低loader被执行的频率。

对于图片文件则没有必要通过include或exclude来降低loader的执行频率,因为无论哪里引入的图片,最后打包都需要通过url-loader对其进行处理,所以include或exclude的语法并不适用于所有loader类型,要根据具体的情况而定。

使用exclude或include可以帮助我们规避对庞大的第三方库文件的处理,但仅通过限定文件处理范围所带来的性能提升其实是有限的,除此之外,如果开启缓存将构建结果缓存到文件系统中,则可让babel-loader的工作效率得到成倍增加,处理方式也很简单,只需为loader增加相应的参数即可:

在这里插入图片描述

3、确保插件的精简和可靠

建议使用webpack官方网站上推荐的插件

4、合理配置 resolve 参数

配置resolve参数可以为我们在编写代码引入模块时提供不少便利,比如使用extensions省略引入JavaScript文件的后缀,使用alias减少书写所引入模块的多目录层级,使用mainFiles声明目录下的默认使用文件等,但当我们使用这些参数带来便利的同时,如果滥加使用也会降低打包速度。

1、可对 js、ts 省略路径,但不要对 css、图片等设置

在这里插入图片描述

可以使用resolve中的extensions属性来申明这些后缀,让项目在构建打包时,由webpack帮我们查找并补全文件后缀。同时对组件路径的引用也可通过resolve的alias配置来进行简化,配置如下:

在这里插入图片描述

如此配置的含义是,当所引入的模块默认了文件后缀时,webpack会在其指定路径下依次查找是否有.js、.jsx、.ts这三种后缀的文件,如果有便使用,并且在模块引入的同时用cpn代替src/component。当有了上述配置后,之前提到的Hello模块的引入便可写成如下形式:

在这里插入图片描述

2、不建议使用 mainFiles 属性

另外,resolve还有一个mainFiles属性,通过对它的配置可以指定让webpack查找引入模块路径下的默认文件名,虽然它能在很大程度上简化模块引用的编码量,但付出的代价是增加了打包构建过程中对目标文件的查找时间,所以不建议使用

5、使用 DllPlugin

前端项目中经常会用到庞大的第三方库,来协助我们完成特定功能的开发,而每当发生修改需要重新进行打包时,webpack会默认去分析所有引用的第三方组件库,最后将其打包进我们的项目代码中。在通常情况下,第三方组件包的代码是稳定的,不更换所引用的版本其代码是不会发生修改的,所以这就给出了一条优化的思路:我们仅需要在第一次打包时去分析这些第三方库,而在之后的打包过程中只需使用之前的结果即可。

这便会用到DllPlugin,它是基于Windows动态链接库(DLL)的思想创建出来的,该插件会把第三方库单独打包到一个文件中,作为一个单纯的依赖库,它不会和我们的项目代码一起参与重新打包,只有当依赖自身发生版本变化时才会重新进行打包。

使用DllPlugin处理文件的过程可分为两步:首先基于动态链接库专属的配置文件打包dll库文件,然后再基于webpack的构建文件打包项目代码。下面以一个简单的React组件为例:

在这里插入图片描述

这里引入了三个在React项目中经常会遇到的第三方包react、react-dom和lodash,如果我们不进行任何处理,每当修改该文件后进行重新打包,则都会引起webpack去分析它们,若打包次数频繁,显然会浪费许多时间。

接下来我们进行优化,具体分为两步,首先将所依赖的第三方库打包成dll文件,然后检查第三方库的版本是否在其后的迭代中发生了变化,若无变化就都使用之前的打包结果。这里可以为第三方库创建单独的配置文件,内容如下:

在这里插入图片描述

该配置文件中将react、react-dom和lodash三个包的包名存储在数组中,并赋值给vendors,其含义是经过打包后,这三个包归于一个名为vendors的包。output中声明了该包的文件名及输出路径,library字段表示该第三方包对外暴露的引用名,即在其他地方可以使用该字段值引用包中的内容。最后使用webpack中的DllPlugin插件对该包中的映射关系进行分析,并将结果输出到指定路径下的json文件。

然后我们需要在webpack主配置文件中,声明对上述打包好的第三方包的使用规则:

在这里插入图片描述

这样配置的意思是首先引入第三方包的打包结果路径,其次引入第三方包的映射关系,当发生重新打包构建时,webpack会首先查看引用的第三方包是否包含在已建立的映射关系文件vendor.manifest.json中,若存在便通过所声明的全局变量vendors去使用,若不存在便去node_modules中获取所需的模块,动态地进行打包操作。

上述方式虽然能够降低重复打包构建的时间,但将项目中所有第三方包都打包进一个文件,势必会使其体积过大从而导致请求过慢,所以在实际项目中,我们也应根据各个第三方包的大小进行拆分,就上述代码而言,可将react和react-dom从vendors中组成为一个新包,代码如下:

在这里插入图片描述

如此在执行webpack构建后会生成四个文件,分别是:vendors包的代码文件vendors.dll.js和其映射关系文件vendors.manifest.json,react包的代码文件react.dll.js和其映射关系文件react.manifest.json,同时对应的主配置文件也需要进行相应的修改,以引入打包的代码文件和映射关系文件。

在这里插入图片描述

这样处理后,既降低了重复构建时的打包时间,又规避了打包成单一文件时,可能由于代码文件体积过大而存在加载过慢的风险。

6、将单进程转化为多进程

webpack是单进程的,就算有多个任务同时存在,它们也只能一个一个排队依次执行,这是nodejs的限制。

但好在大多数CPU已经都是多核的了,我们可以使用happypack充分释放CPU在多核并发方面的优势,帮助我们把打包构建任务分解成多个子任务去并发执行,这将大大提高打包的效率。其使用方法也很简单,就是将原有的loader配置转移到happypack中去处理:

在这里插入图片描述

7、压缩打包结果的体积
1、删除冗余代码

webpack从2.0版本开始,便基于ES6推出了Tree-shaking,它能根据import、export的模块导入导出语法,在构建编译过程中分析每个模块是否被真实使用,对于没用到的代码,会在最后的打包结果中删除。

容易看出Tree-shaking对处理模块级的代码冗余比较擅长,但对更细粒度的代码冗余,比如console语句、注释等,可能就需要在CSS和JavaScript压缩过程中进行处理了,常用的方式是通过uglifyjs-webpack-plugin来实现的,具体配置方式如下:

在这里插入图片描述

2、代码拆分按需加载

项目源代码也需要拆分,可以根据路由来划分打包文件,当访问到不同路由时再触发相应回调请求打包文件,对于webpack的打包输出的配置如下:

在这里插入图片描述

以React项目为例,在配置路由时还需添加如下内容:

在这里插入图片描述

此处的关键方法就是require.ensure这个异步方法,webpack会将我们这里定义的组件单独打包成一个文件,仅当路由跳转到mine时,才会触发回调去获取MyComponent组件的内容。

3、可视化分析

分析打包结果的可视化工具:webpack-bundle-analyzer。它的分析结果会生成一个文件大小图:

该插件的工作原理也比较简单,就是分析在compiler.plugin(‘done’,function(stats));时传入的参数stats,它是webpack的一个统计类Stats的实例,然后通过对实例调用toJson()方法转成json文件,再从中提取出chunks各个包的大小信息,最后在Canvas中进行画图。通过该图能让开发者快速意识到哪些模块异常的大,然后找出过大的原因去优化它。

在这里插入图片描述

webpack-bundle-analyzer分析结果

该工具使用起来也很方便,就如使用普通插件一样:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1566947.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++ //练习 11.3 编写你自己的单词计数程序。

C Primer(第5版) 练习 11.3 练习 11.3 编写你自己的单词计数程序。 环境:Linux Ubuntu(云服务器) 工具:vim 代码块 /*************************************************************************> …

当Pycharm中右键运行python程序时出现Run ‘pytest in tests ***py‘,如何解决?

1、在Pycharm中右键运行python程序时出现Run pytest in tests ***py ,这是进入了Pytest模式。 2、解决办法 进入到File->Settings->Tools->Python integrated Tools页面或者快捷键(CtrlAltS) 找到Testing下的Default test runner …

ES学习日记(十)-------Java操作ES之连接客户端

Elasticsearch有两种连接方式: transport、rest。transport 通过TCP方式访问ES(只支持iava),rest 方式通过http API 访问ES(没有语言限制)。 ES官方建议使用Iest 方式,transport 在7.8 版本中不建议使用,在8.x的版本中废弃。你可以用Java客户…

elementui 左侧或水平导航菜单栏与main区域联动

系列文章目录 一、elementui 导航菜单栏和Breadcrumb 面包屑关联 二、elementui 左侧导航菜单栏与main区域联动 三、elementui 中设置图片的高度并支持PC和手机自适应 四、elementui 实现一个固定位置的Pagination(分页)组件 文章目录 系列文章目录…

力扣1379---找出克隆二叉树的相同节点(Java、DFS、简单题)

目录 题目描述: 思路描述: 代码: (1): (2): 题目描述: 给你两棵二叉树,原始树 original 和克隆树 cloned,以及一个位于原始树 ori…

天眼护航 安全无界:天通哨兵PS02—电力巡检保护的智能利器

在电力行业中,输电线路的安全稳定运行对于保障社会经济活动至关重要。然而,广阔的输电线路常常穿越复杂的地形和恶劣的自然环境,给电力巡检和保护工作带来了巨大挑战。 为了提高巡检效率和响应速度,更好地保障电力设施的安全运行…

Redis底层数据结构-IntSet

IntSet是Redis中set集合的一种实现方式,基于整数数组来实现,并具备长度可变,有序等特征。 结构如下图所示: int8_t contents[]的元素只有一个字节,能表示的整数范围只有-128~127,是不是有点小呀&#xf…

简单的安全密码生成器PwGen

什么是 PwGen ? PwGen 是一个简单的 Docker Web 应用程序,旨在生成具有可自定义选项的安全密码或密码短语。用户可以选择生成具有特定标准的随机密码或由随机单词组成的密码。其他功能包括在密码中包含大写字母、数字和特殊字符的选项,或者将…

丰诺畅机电科技将莅临2024年第13届生物发酵展

参展企业介绍 无锡丰诺畅机电科技有限公司,是一家分离设备专业制造公司,集开发、设计、制造、销售、服务于一体;具有专业的生产技术,先进的生产工艺,精良的制造设备,完善的检测手段;为满足不同用户的过滤需求&#xf…

单例模式(加深版)

单例模式(加深版) 饿汉模式 缺点:造成资源的浪费 示例: ## 懒汉模式 示例: 枚举类型单例模式 示例:

中兴R5300 G4服务器BMC远程管理启用防火墙设置

中兴R5300 G4服务器启用了BMC远程管理,为保障设备安全配置启用防火墙只放通特定维护服务器可以访问。 一、设备初始登录信息 1、中兴R5300G4服务器,默认BMC管理地址https//192.168.5.7 2、中兴zteroot用户的默认密码为:Superuser9! 二、…

开启 Keep-Alive 可能会导致http 请求偶发失败

大家好,我是蓝胖子,说起提高http的传输效率,很多人会开启http的Keep-Alive选项,这会http请求能够复用tcp连接,节省了握手的开销。但开启Keep-Alive真的没有问题吗?我们来细细分析下。 最大空闲时间造成请求…

.Net Core/.Net6/.Net8 ,启动配置/Program.cs 配置

.Net Core/.Net6/.Net8 &#xff0c;启动配置/Program.cs 配置 没有废话&#xff0c;直接上代码调用 没有废话&#xff0c;直接上代码 /// <summary>/// 启动类/// </summary>public static class Mains{static IServiceCollection _services;static IMvcBuilder _…

部署k8s客户端,及docker私仓部署

1.部署一个docker私仓 mkdir /opt/docker/registry #配置仓库密码 mkdir /opt/docker/auth cd /opt/docker/auth htpasswd -Bbn admin admin > htpasswd#运行docker私仓服务&#xff0c;下面端口5000:5000 前面的5000对应本机端口可以自定义 docker run -itd \ -v /opt/d…

Django複習總結

①Django是框架。那麼什麼是框架&#xff1a; 框架很像是一個骨架&#xff0c;帶有很多默認器官的骨架。我們可以根據需要改寫、複寫這些器官。 從而實現自己所需要的功能。 ②Django是MVC模型\MVT模型&#xff1a; MVC模型&#xff1a;M&#xff1a;models模型層 V&#…

redis开源协议变更了?我们还能用吗?

Redis是一款广泛使用的开源键值存储数据库&#xff0c;其开源协议的变更引起了社区和行业的广泛关注。根据搜索结果&#xff0c;Redis Labs宣布Redis将采用双重源代码可用许可证&#xff08;RSALv2&#xff09;和服务器端公共许可证&#xff08;SSPLv1&#xff09;&#xff0c;…

HTML - 你是如何理解link和@import的

难度级别:初级及以上 提问概率:55% link是我们非常熟悉的一个HTML标签,用于引入CSS文件,而@import则存在于CSS文件内部,用于再次引入其他的CSS文件。所以很显然,执行顺序上,link标签会随着HTML文档加载,开始触发下载CSS文件的操作。…

tomcat-连接器架构设计

一、NioEndpoint组件 Tomcat的NioEndPoint组件实现了I/O多路复用模型&#xff0c;接下来我会介绍NioEndpoint的实现原理。 1.总体工作流程 我们知道&#xff0c;对于Java的多路复用器的使用&#xff0c;无非是两步&#xff1a; 1.创建一个Seletor&#xff0c;在它身上注册各…

Spring MVC 的执行流程

Spring MVC 的执行流程 1、用户输入 URL 或 点击链接&#xff0c;浏览器将发送 HTTP 请求到服务器 2、请求首先到达 Spring MVC 的前端控制器 DispatcherServlet 3、前端控制器通过处理器映射器 HandlerMapping 根据请求 URL 找到对应的处理器 handler 4、前端控制器使用处理…

三星、SK hynix和美光三大厂商DRAM单片晶圆容量趋势

随着DRAM单元尺寸的不断缩小&#xff08;也就是制程微缩&#xff09;&#xff0c;晶圆级&#xff08;12英寸&#xff09;DRAM的最大容量持续增加。 特别值得一提的是&#xff0c;美光最近推出的D1-beta 16 Gb DDR5芯片&#xff0c;使得每片晶圆的最高容量达到了3.61 TB&#xf…