## 一、资源的合并和压缩
`web`前端应用的开发与部署过程:
![image-20200330221214962](https://user-gold-cdn.xitu.io/2020/4/2/1713ac1c67248a4d?w=1064&h=601&f=png&s=332796)
输入`url`到页面显示出来的过程:
![image-20200330221250064](https://gitee.com/ahuntsun/BlogImgs/raw/master/前端性能优化/2.png)
请求过程中一些潜在的性能优化点:
* `dns`是否可以通过缓存减少`dns`查询时间?
* 网络请求的过程如何走最近的网络环境?
* 相同的静态资源是否可以缓存?
* 能否减少`http`请求的大小和次数?
* 能否进行服务端渲染?
**总结:**深入理解`http`请求的过程是前端性能优化的核心。
**优化核心**
* 减少`http`请求数量;
* 减少请求资源的大小;
**`google`首页案例学习**
* `html`压缩;
* `css`压缩;
* `js`的压缩和混乱;
* 文件合并;
* 开启`gzip`;
### 1.`html`压缩
`HTML`代码压缩就是压缩一些在文本文件中有意义,但是在`HTML`中**不显示**的字符,包括**空格**,**制表符**,**换行符**等,还有一些其他意义的字符,如**`HTML`注释**也可以被压缩;
**一个简单的计算:**
`google`的流量,占到整个互联网的`40%`,预计`2016`年全球网络流量将达到`1.3ZB(1ZB = 10^9TB)`,那么`google`在`2016`年的流量就是`1.3ZB * 40%`,如果`google`每`1MB`请求减少一个字节,**每年可以节省流量近`500TB`**流量。
**如何进行`html`压缩**
- 使用在线网站进行压缩;
- `nodejs`提供的`html-minifier`工具;
- 后端模板引擎渲染压缩;
### 2.`css`代码压缩
分为两部分:
* 无效代码的压缩;
* `css`语义合并;
**如何进行`css`压缩**
* 使用在线网站进行压缩;
* 使用`html-minifier`对`html`中的`css`进行压缩;
* 使用`clean-css`对`css`进行压缩;
### 3.`js`压缩与混乱(丑化)
包括:
* 无效字符的删除(空格,回车等);
* 剔除注释;
* 代码语义的缩减和优化;
* 代码保护(如果代码不经处理,客户端可直接窥探代码漏洞);
**`JS`压缩与混乱(丑化)**
* 使用在线网站进行压缩:https://tool.oschina.net/jscompress/
![image-20200330230443570](https://user-gold-cdn.xitu.io/2020/4/2/1713ac1c6d13fd18?w=1318&h=514&f=png&s=51886)
* 使用`html-minifier`对`html`中的`js`进行压缩;
* 使用`uglify.js2`对`js`进行压缩;
### 4.文件合并
文件合并的好处:
![image-20200330224139300](https://user-gold-cdn.xitu.io/2020/4/2/1713ac1c6ddc2008?w=752&h=584&f=png&s=244674)
左边的表示使用`http`长链接`keep-alive`但不合并请求的情况,需要分三次去获取`a.js`、`b.js`、`c.js`;右边是使用长链接并且合并请求的情况,只需要发送一次获取合并文件`a-b-c.js`的请求,就能将三个文件都请求回来。
不合并请求有下列缺点:
* 文件与文件之间有插入的上行请求,会增加`N-1`个网络延迟;
* 受丢包问题的影响更严重:因为每次请求都可能出现丢包的情况,减少请求能有效减少丢包情况;
* `keep-alive`本身也存在问题:经过代理服务器时可能会被断开;
**文件合并存在的问题**
* 首屏渲染问题:当请求`js`文件的时候,如果页面渲染只依赖`a.js`文件,由于文件合并,需要等待合并后的`a-b-c.js`文件请求回来才能继续渲染,这样就会导致页面渲染速度变慢。这种情况大多出现在现代化的前端框架,如`Vue`等的使用过程中;
* 缓存失效问题:合并后的文件`a-b-c.js`中只要其中一个文件(比如`a.js`)发生变化,那么整个合并文件都将失效,而不采用文件合并就不会出现这种情况;
**使用建议**
* 公共库合并:将不经常发生变化的公共组件库文件进行合并;
* 将不同页面的`js`文件单独合并:比如在单页面应用`SPA`中,当路由跳转到具体的页面时才请求该页面需要的`js`文件;
**如何进行文件合并**
* 使用在线网站进行文件合并;
* 使用`nodejs`实现文件合并;
* 使用`webpack`等前端构件化工具也可以很好地实现;
## 二、图片相关的优化
**有损压缩过程:**
![image-20200330232432166](https://user-gold-cdn.xitu.io/2020/4/2/1713ac1c6f5a52c3?w=1102&h=571&f=png&s=215654)
一张`JPG`图片的解析分别要进行:
* 颜色空间的转换:从`RGB`的颜色空间转到其他的颜色空间 ;
* 进行重采样:区分高频和低频的颜色变换;
* 进行`DCT`过程:对高频的颜色采样结果进行压缩,这样压缩的收益会比较大;
* 再对数据进行量化;
* 最后进行编码(`encoding`);
最终得到`JPEG-Compressed Image Data`,即真正显示出来的`JPG`图片。虽然这是一种有损压缩,但是很多情况下,这些损失的数据并不影响显示;
**`png8/png24/png32`之间的区别**
* `png8`:`256`色 `+` 支持透明;
* `png24`:`2^24`色 `+` 不支持透明;
* `png32`:`2^32`色 `+` 支持透明;
**不同格式图片常用的业务场景**
* `jpg`有损压缩,压缩率高,支持透明;应用:大部分不需要透明图片的业务场景;
* `png`支持透明,浏览器兼容好;应用:大部分需要透明图片的业务场景;
* `webp`(`2010`年由谷歌推出)压缩程度更好,在`ios webview`中有兼容性问题;应用:安卓全部;
* `svg`矢量图,代码内嵌,相对较小,用于图片样式相对简单的场景;应用:比如`logo`和`iconfont`;
### 1.图片压缩
针对真实图片情况,舍弃一些相对无关紧要的色彩信息,对图片进行压缩;比如在线压缩网站:https://tinypng.com/
![image-20200331102612277](https://user-gold-cdn.xitu.io/2020/4/2/1713ac1c777fd86d?w=1312&h=671&f=png&s=610754)
### 2.`css`雪碧图
将网站上用到的一些图片整合到一张单独的图片中,从而减少网站`HTTP`请求数量。原理为:设定整张雪碧图可示区域,将想要显示的图标定位到该处(左上角);**缺点:**整合图片比较大时,一次加载比较慢。
如天猫的雪碧图:
![image-20200331100812635](https://user-gold-cdn.xitu.io/2020/4/2/1713ac1cabcab8f3?w=955&h=521&f=png&s=71525)
很多情况下,并不是所有的小图标都放在一张雪碧图中,而是会适当进行拆分。现在使用雪碧图的场景比较少了。
自动生成雪碧图样式的网站:http://www.spritecow.com/
![image-20200331111632363](https://user-gold-cdn.xitu.io/2020/4/2/1713ac1cae1c4e75?w=1140&h=785&f=png&s=363553)
选中雪碧图中对应的图标,就会生成对应的样式。
### 3.网页内联图片(`Image inline`)
将图片的内容内嵌到`html`当中,减少网站的`HTTP`请求数量,常用于处理小图标和背景图片。网页内联图片写法为:
```
```
浏览器上的表现形式为:
![image-20200331105927649](https://user-gold-cdn.xitu.io/2020/4/2/1713ac1cbdabaf38?w=1054&h=325&f=png&s=76209)
这里提供一个将:`image` 转 `DataUrI`的网址:http://tool.c7sky.comhttps://img.qb5200.com/download-x/datauri/
![image-20200331105719573](https://user-gold-cdn.xitu.io/2020/4/2/1713ac1cb0bdca9b?w=1030&h=586&f=png&s=77587)
**缺点:**
- 浏览器不会缓存内联图片资源;
- 兼容性较差,只支持`ie8`以上浏览器;
- 超过`1000kb`的图片,`base64`编码会使图片大小增大,导致网页整体下载速度减慢;
所以要根据场景使用,不过内联图片**减少`HTTP`请求**的优点还是很显著的。比如,在开发中小于`4KB`或`8KB`的图片都会通过构建工具自动`inline`到`HTML`中,这种情况下`Image inline`带来的图片大小增长其实是比增加`HTTP`请求次数更优的。
### 4.矢量图`SVG`与`iconfont`
**使用`iconfont`解决`icon`问题**
应尽量使用该方式,比如可以采用阿里巴巴矢量图库:
![image-20200331102136261](https://user-gold-cdn.xitu.io/2020/4/2/1713ac1cb4385403?w=1018&h=421&f=png&s=24626)
可以选择格式进行下载:
![image-20200331102259285](https://user-gold-cdn.xitu.io/2020/4/2/1713ac1cbb0d2a3d?w=1018&h=732&f=png&s=42482)
可以看到它们的大小有着明显的差异:
![image-20200331102353635](https://user-gold-cdn.xitu.io/2020/4/2/1713ac1cf1bd08bf?w=768&h=57&f=png&s=8963)
**使用`SVG`进行矢量图的控制**
`SVG `意为可缩放矢量图形(`Scalable Vector Graphics`)。`SVG` 使用 `XML` 格式定义图像。下为示例:
![image-20200331112542540](https://user-gold-cdn.xitu.io/2020/4/2/1713ac1cf24b39af?w=1230&h=406&f=png&s=35645)
在线转换网站:http://www.bejson.com/convert/image_to_svg/
![image-20200331112802316](https://user-gold-cdn.xitu.io/2020/4/2/1713ac1cf23f75ed?w=1418&h=799&f=png&s=114803)
### 5.`webp`
`webp`的优势体现在它具有更优的图像压缩算法,能带来更小的图片体积,而且拥有肉眼识别无差异的图像质量;同时具备了无损和有损的压缩模式、`Alpha`透明以及动画的特性。在`JPEG`和`PNG`上的转化效果都非常优秀、稳定和统一。安卓上不存在兼容性问题,推荐安卓下使用。
以下为淘宝网首页请求的图片:
![image-20200331101234068](https://user-gold-cdn.xitu.io/2020/4/2/1713ac1d020b8613?w=965&h=305&f=png&s=121907)
可以看到,图片中大量地添加了`webp`格式的选择。`.jpg_.webp`表示当浏览器支持`webp`时采用`webp`格式,否则采用`jpg`格式。
下面为`B`站首页的图片,可以看到基本都采用了`webp`格式:
![image-20200401110811561](https://user-gold-cdn.xitu.io/2020/4/2/1713ac1d0ed2bb59?w=991&h=604&f=png&s=180232)
同一张图片`jpg`格式和`webp`格式压缩率有着明显的差异:
![image-20200331101633934](https://user-gold-cdn.xitu.io/2020/4/2/1713ac1d117540e8?w=747&h=56&f=png&s=8490)
可以通过在线网站将图片转换为`webp`:https://zhitu.isux.us/
![image-20200331103230221](https://user-gold-cdn.xitu.io/2020/4/2/1713ac1d274f721d?w=1267&h=782&f=png&s=766897)
像图片这样的静态文件可以存放在`CDN`服务器上,让`CDN`服务器批量地将图片转换成`Webp`格式;
## 三、浏览器渲染引擎与阻塞
### 1.渲染的主要模块
版本一:
![image-20200331131925229](https://user-gold-cdn.xitu.io/2020/4/2/1713ac1d2e30d383?w=817&h=391&f=png&s=93555)
版本二:
![image-20200331113305758](https://user-gold-cdn.xitu.io/2020/4/2/1713ac1d3b852d62?w=1167&h=264&f=png&s=134420)
一个渲染引擎主要包括:`HTML`解析器,`CSS`解析器,`javascript`引擎,布局`layout`模块,绘图模块:
* `HTML`解析器:解释`HTML`文档的解析器,主要作用是将`HTML`文本解释成`DOM`树;
* `CSS`解析器:它的作用是为`DOM`中的各个元素对象计算出样式信息,为布局提供基础设施;
* `Javascript`引擎:使用`Javascript`代码可以修改网页的内容,也能修改`css`的信息,`javascript`引擎能够解释`javascript`代码,并通过`DOM`接口和`CSS`树接口来修改网页内容和样式信息,从而改变渲染的结果;
* 布局(`layout`):在`DOM`创建之后,`Webkit`需要将其中的元素对象同样式信息结合起来,计算他们的大小位置等布局信息,形成一个能表达这所有信息的内部表示模型;
* 绘图模块(`paint`):使用图形库将布局计算后的各个网页的节点绘制成图像结果;
### 2.渲染过程
浏览器渲染页面的整个过程:浏览器会从上到下解析文档。
2. 浏览器解析时遇见 `HTML` 标记,就会调用`HTML`解析器解析为对应的 `token` (一个`token`就是一个标签文本的序列化)并构建 `DOM` 树(就是一块内存,保存着`tokens`,建立它们之间的关系)。在生成`DOM`的最开始阶段(应该是`Bytes` → `characters`后),并行发起`css`、图片、`js`的请求,无论他们是否在`HEAD`标签中。
> 注意:发起`js`文件的下载请求(`request`)并不需要`DOM`处理到那个`script`节点;
3. 遇见 `style/link` 标记 调用解析器 处理 `CSS` 标记并构建 `CSS`样式树;
4. 遇见 `script` 标记 调用 `javascript`解析器处理`script`标记,绑定事件、修改`DOM`树/`CSS`树等;
5. 将 `DOM`树 与 `CSS`树 合并成一棵渲染树(`Render Tree`)。
6. 布局(`Layout`):根据渲染树中各节点的样式和依赖关系,计算出每个节点在屏幕中的位置;
7. 绘图(`Painting`):按照计算出来的结果:要显示的节点、节点的`CSS`与位置信息,通过显卡,把内容画到屏幕上;
经过第一次`Painting`之后`DOM`、`CSSOM`、`Render Tree`都可能会被多次更新,比如`JS`修改了`DOM`或者`CSS`属性时,`Layout`和`Painting`就会被重复执行。除了`DOM`、`CSSOM`更新的原因外,图片下载完成后也需要调用`Layout` 和 `Painting`来更新网页。
> **补充:**
>
> * `HTML`中可能会引入很多的`css、js`这样的外部资源,这些外部资源在浏览器端是并发加载的。但是浏览器会对同一域名进行并发数量(度)的限制,即单个域名的并发度是有限的;
> * 所以,经常将大部分的资源托管到`CDN`服务器上,并且设置`3~4`个`CDN`域名。防止只有一个`CDN`域名的情况下,达到了浏览器外部资源并发请求数目的上限,导致很多资源无法做到并发请求。所以,应设置多个`CDN`域名;
### 3.`css`阻塞
只有通过`link`引入的外部`css`才会产生阻塞:
* **`style`标签中的样式:**
* 由`html`解析器进行解析;
* 不阻塞浏览器渲染(可能会产生“闪屏现象”);
* 不阻塞`DOM`解析;
* **`link`引入的外部`css`样式(推荐使用的方式):**
* 由`CSS`解析器进行解析;
* 阻塞浏览器渲染:由于`css`已经加载完毕,所以整个渲染过程是带样式的,所以这种阻塞可以避免“闪屏现象”;
* 阻塞其后面的`js`语句的执行:这个不难理解,`js`文件中经常会出现`DOM`操作,操作过程中有可能涉及到`css`样式的修改。实际上,这些修改往往是依赖于之前引入的`css`设定的样式的,所以`css`会阻塞`js`的执行;
* 不阻塞DOM的解析;
* **优化核心理念:尽可能快的提高外部`css`加载速度:**
* 使用`CDN`节点进行外部资源加速;
* 对`css`进行压缩(利用打包工具,比如`webpack`,`gulp`等);
* 减少`http`请求数,将多个`css`文件合并;
* 优化样式表的代码;
### 4.`js`阻塞
* **阻塞DOM解析:**
原因:浏览器不知道后续脚本的内容,如果先去解析了下面的`DOM`,而随后的`js`删除了后面所有的`DOM`,那么浏览器就做了无用功,浏览器无法预估脚本里面具体做了什么操作,例如像`document.write`这种操作,索性全部停住,等脚本执行完了,浏览器再继续向下解析`DOM`;可以通过给`script`标签添加`defer`和`async`属性,异步引入`js`文件,以此来解决这一问题。
* **阻塞页面渲染:**
原因:`js`中也可以给`DOM`设置样式,浏览器同样等该脚本执行完毕,再继续干活,避免做无用功;
* **阻塞后续`js`的执行:**
原因:`js`是按顺序执行的,这样可以维护依赖关系,例如:必须先引入`jQuery`再引入`bootstrap`;
* **不阻塞资源的加载:**
这并不与上面矛盾,因为不可能由于加载一个`js`文件就把其他资源的加载都阻塞了。针对这种常见的情况,浏览器会通过预加载的方式加载后续的资源;
### 5.总结
* `css`的解析和`js`的执行是互斥的(互相排斥),`css`解析的时候`js`停止执行,`js`执行的时候`css`停止解析;
* 无论`css`阻塞,还是`js`阻塞,都不会阻塞浏览器**加载**外部资源(图片、视频、样式、脚本等);
因为览器始终处于一种:“先把请求发出去”的工作模式,只要是涉及到网络请求的内容,无论是:图片、样式、脚本,都会先发送请求去获取资源,至于资源到本地之后什么时候用,由浏览器自己协调。显然这种做法效率很高;
* `WebKit` 和`Firefox` 都进行了【**预解析**】这项优化。在执行`js`脚本时,浏览器的其他线程会解析文档的其余部分,找出并加载需要通过网络加载的其他资源。通过这种方式,资源可以在并行连接上加载,从而提高总体速度。请注意,预解析器不会修改 `DOM` 树
## 四、懒加载和预加载
### 1.懒加载
图片进入可视区域之后再请求图片资源的方式称为图片懒加载。适用于图片很多,页面很长的业务场景,比如电商;
**懒加载的作用:**
* **减少无效资源的加载:**
比如一个网站有十页图片,用户只查看了第一页的图片,这就没必要将十页图片全都加载出来;
* **并发加载的资源过多会阻塞`js`的加载,影响网站正常的使用:**
由于浏览器对某一个`host name`是有并发度上限的,如果图片资源所在的`CDN`和静态资源所在的`CDN`是同一个的话,过多图片的并发加载就会阻塞后续`js`文件的并发加载。
**懒加载实现的原理:**
监听`onscroll`事件,判断可视区域位置:
图片的加载是依赖于`src`路径的,首先可以为所有懒加载的静态资源添加自定义属性字段,用于存储真实的`url`。比如是图片的话,可以定义`data-src`属性存储真实的图片地址,`src`指向`loading`的图片或占位符。然后当资源进入视口的时候,才将`src`属性值替换成`data-src`中存放的真实`url`。
```html
```
**懒加载实例**
可以使用元素的`getBoundingRect().top`来判断当前位置是否在视口内,也可以使用元素距离文档顶部的距离`offsetTop`和`scrollTop`是否小于视口高度来判断:
![image-20200331153831469](https://user-gold-cdn.xitu.io/2020/4/2/1713ac1d3dd88bc2?w=770&h=657&f=png&s=24377)
**举例**
比如手机淘宝首页:
![image-20200331145210486](https://user-gold-cdn.xitu.io/2020/4/2/1713ac1d3f33f519?w=1627&h=803&f=png&s=543095)
当快要滚动到需要展示的图片时才进行图片的请求,可以看到图片上有一个`lazyload`的属性:
![image-20200331145756313](https://user-gold-cdn.xitu.io/2020/4/2/1713ac1d751162c7?w=481&h=83&f=png&s=11410)
### 2.预加载
预加载与懒加载正好是相反的过程:**懒加载**实际上是延迟加载,将我们所需的静态资源加载时间延后;而**预加载**是将图片等静态资源在使用之前的提前请求,这样资源在使用到时能从缓存中直接加载,从而提升用户体验;
**预加载的作用:**
* **提前请求资源,提升加载速度**:使用时只需要读取浏览器缓存中提前请求到的资源即可;
* **维护页面的依赖关系**:比如`WebGL`页面,会依赖一些`3D`模型,这些都是页面渲染所必须的资源。如果资源都没有加载完毕就进行页面的渲染,就会造成非常不好的体验。
所以时常使用预加载的方式维护页面渲染的依赖关系,比如将`WebGL`页面依赖的`3D`模型加载完之后才进行页面渲染。这样渲染的过程就不会有任何阻碍,具有较好的用户体验;
**预加载的实例**
例如九宫格抽奖业务,每个奖品都有一个选中态和非选中态,实际上这是由两张图片组合而成的。由于每个奖品的选中过程都是一瞬间,这就对图片的选中态和非选中态切换效率要求很高,如果选中态的图片没有预加载的话显然是来不及的。
![image-20200331150725347](https://user-gold-cdn.xitu.io/2020/4/2/1713ac1d4f47acaa?w=631&h=577&f=png&s=219895)
所以,实际上对于九宫格中所有图片选中态的样式和对应的图片都需要进行预加载,从而让我们在抽奖的过程中,能够瞬间从缓存中读取到选中态的图片,从而不影响抽奖效果的展示。
除此之外还有网站登录或活动时需要用到的动画,这是在动画需要的每帧图片都完全预加载完之后才会进行显示的。
## 五、重绘与回流
### 1.`CSS`图层
浏览器在渲染一个页面时,会将页面分为很多个图层,图层有大有小,每个图层上有一个或多个节点。在渲染 `DOM`的时候,浏览器所做的工作实际上是:
1、获取`DOM`后分割为多个图层;
2、对每个图层的节点计算样式结果(`Recalculate style`--样式重计算);
3、为每个节点生成图形和位置(`Layout`--回流和重布局);
4、将每个节点绘制填充到图层位图中(`Paint Setup`和`Paint`--重绘);
5、图层作为纹理上传至`GUI`;
6、复合多个图层到页面上生成最终屏幕图像(`Composive Layers`--图层重组);
### 2.创建图层的条件
* 拥有`3D`或透视变换的`css`属性(`prespective transform` );
* 使用加速视频解码的`