前端性能优化高频问题

302 阅读8分钟

1、浏览器加载原理

  1. 当浏览器从服务器接收到了HTML文档,并把HTML在内存中转换成DOM树,在转换的过程中如果发现某个节点(node)上引用了 CSS 或者 IMAGE,就会再发1个request去请求 CSS 或 IMAGE,然后继续执行下面的转换,而不需要等待 request 的返回,当 request 返回后,只需要把返回的内容放入到 DOM 树中对应的位置就 OK 。
  2. 但当引用了JS的时候,浏览器发送1个 js request 就会一直等待该 request 的返回。
  3. 因为浏览器需要一个稳定的DOM树结构,而JS很有可能有代码直接改变了DOM树结构,浏览器为了防止出现JS修改DOM树需要重新构建DOM树的情况,所以 就会阻塞其他的下载和呈现

2、缓存

强缓存:Expires & Cache-Control

  • xpires:指定了在浏览器上缓冲存储的页距过期还有多少时间,等同Cache-control中的max-age的效果,如果同时存在,则被 Cache-Control 的 max-age 覆盖。

  • Cache-Control:

    • public:响应被缓存,并且在多用户间共享。
    • private:默认值,响应只能够作为私有的缓存(e.g., 在一个浏览器中),不能再用户间共享;
    • no-cache:响应不会被缓存,而是实时向服务器端请求资源。
    • max-age:数值,单位是秒,从请求时间开始到过期时间之间的秒数。基于请求时间(Date字段)的相对时间间隔,而不是绝对过期时间

协商缓存:Last-Modified & Etag

  • Last-Modified/If-Modified-Since:本地文件在服务器上的最后一次修改时间。缓存过期时把浏览器端缓存页面的最后修改时间发送到服务器去,服务器会把这个时间与服务器上实际文件的最后修改时间进行对比,如果时间一致,那么返回304,客户端就直接使用本地缓存文件。

  • Etag/If-None-Match:(EntityTags)是URL的tag,用来标示URL对象是否改变,一般为资源实体的哈希值。和Last-Modified类似,如果服务器验证资源的ETag没有改变(该资源没有更新),将返回一个304状态告诉客户端使用本地缓存文件Etag的优先级高于Last-ModifiedEtag 主要为了解决 Last-Modified 无法解决的一些问题

    • 文件也许会周期性的更改,但是他的内容并不改变,不希望客户端重新get;
    • If-Modified-Since能检查到的粒度是s级;
    • 某些服务器不能精确的得到文件的最后修改时间。

3、懒加载与预加载

懒加载

  • 图片进入可视区域之后请求图片资源。
  • 对于电商等图片很多,页面很长的业务场景适用。
  • 减少无效资源的加载。
  • 并发加载的资源过多会阻塞js的加载,影响网站的正常使用。 img src被设置之后,webkit解析到之后才去请求这个资源。所以我们希望图片到达可视区域之后,img src才会被设置进来,没有到达可视区域前并不现实真正的src,而是类似一个1px的占位符。

预加载

  • 图片等静态资源在使用之前的提前请求。
  • 资源使用到时能从缓存中加载,提升用户体验。
  • 点击操作前预先加载下一屏数据。

4、HTTP/2.0和HTTP/1.0

二进制分帧

HTTP/2 采用二进制格式传输数据,而非 HTTP 1.x 的文本格式,二进制协议解析起来更高效。 HTTP / 1 的请求和响应报文,都是由起始行,首部和实体正文(可选)组成,各部分之间以文本换行符分隔。HTTP/2 将请求和响应数据分割为更小的帧,并且它们采用二进制编码。

HTTP/2 中,同域名下所有通信都在单个连接上完成,该连接可以承载任意数量的双向数据流。 每个数据流都以消息的形式发送,而消息又由一个或多个帧组成。多个帧之间可以乱序发送,根据帧首部的流标识可以重新组装。

多路复用

HTTP 1.x 中,如果想并发多个请求,必须使用多个 TCP 链接,且浏览器为了控制资源,还会对单个域名有 6-8个的TCP链接请求限制

HTTP/2.0

同个域名只需要占用一个 TCP 连接

单个连接可以承载任意数量的双向数据流

服务器推送

服务端可以在发送页面HTML时主动推送其它资源,而不用等到浏览器解析到相应位置,发起请求再响应。例如服务端可以主动把JS和CSS文件推送给客户端,而不需要客户端解析HTML时再发送这些请求。

头部压缩

HTTP/2对这些首部采取了压缩策略

  • HTTP/2在客户端和服务器端使用“首部表”来跟踪和存储之前发送的键-值对,对于相同的数据,不再通过每次请求和响应发送;
  • 首部表在HTTP/2的连接存续期内始终存在,由客户端和服务器共同渐进地更新;
  • 每个新的首部键-值对要么被追加到当前表的末尾,要么替换表中之前的值。

5、合理使用Icon Font

IconFont技术起源于Web领域的Web Font技术,它是把一些简单的图标制作成字体,然后让图标变成和字体一样使用,Icon 的设计和使用在近几年的发展中,也经历了由当初的 img 方案 到现如今的 svg 方案,有以下优点:

  • 字体是矢量的,所以可以随意改变大小。
  • 因为它是字体,所以所有字体的css都可以使用,比如font-size,color,background,opacity等。
  • 减少图片请求数。
  • iconfont没有兼容性问题,IE6,Android2.3都能够兼容

6、使用 CDN

CDN 是将源站内容分发至最接近用户的节点,使用户可就近取得所需内容,提高用户访问的响应速度和成功率

7、服务端渲染(SSR)优化首屏时间

在前后端分离之后,后端语言的模板功能被弱化,整个页面的渲染基本上都由前端 js 动态渲染,但这样对于一些应用来说是有缺陷的。比如需要 SEO 的,需要打开页面不用等待就能看到页面的,另外前端页面展示过度依赖js和css逻辑执行,在极端情况或者网络较差,手机性能低下(尤其在低端Android机型较为明显)时,白屏时间较长,这时服务端渲染便应用而生,至于为什么是Nodejs,作为一个前端,难道还要用Java么。。?

为什么会有服务端渲染?

如果你说服务端渲染和早期web框架,例如SSH,JSP servlet,PHP等等一样的话,那我只能说呵呵,目前的服务端渲染和早期的框架是有本质区别的:

  • Web 2.0时代最大的思想革命本质不是前后端分离,而是把网页当作独立的应用程序(app)。建立在前后端分离的基础上,后端只负责提供数据json格式,前端还是负责页面交互逻辑,大多数的服务端渲染采用Nodejs层来进行数据组装,html拼接。
  • 重点在首屏!!首屏时间的优化,移动互联网时代的爆发,用户对网页性能的要求越来越高,但毕竟基于3G,4G网络,让用户更快的看到页面就能挽留更多的潜在商机。服务端只负责首屏的页面渲染,真正过了首屏,大多数的业务逻辑,页面交互,还是需要有单独的前端来实现的。

8、重绘和重排

在上面浏览器需要做的这些事情中,会引发不同程度的重绘和重排,而重绘和重排正式影响流畅的重要因素:

部分渲染树(或者整个渲染树)需要重新分析并且节点尺寸需要重新计算,这被称为重排(回流) 。 1. 由于节点几何属性发生改变或者由于样式发生改变,例如改变元素背景色时,屏幕上的部分内容需要更新,这样的更新被称为重绘

重排和重绘代价是高昂的,它们会破坏用户体验,并且让UI展示非常迟缓,但是每次重排,必然会导致重绘,而每次重绘并不一定会发生重排,我们需要在以下几种场景来减少重排的发生:当页面布局和几何属性改变时就需要重排(回流)。下述情况会发生浏览器重排(回流):

添加、删除可见的DOM元素

元素位置改变。

元素尺寸改变——边距、填充、边框、宽度和高度。

内容改变——比如文本改变或者图片大小改变而引起的计算值宽度和高度改变

页面渲染初始化。

浏览器窗口尺寸改变——resize事件发生时。

9、内存泄漏

  • 闭包里面变量被使用
  • 定时器没有销毁
  • console.log
  • 全局变量
  • dom 事件执行回调,dom对象要放在回调里面定义,这样事件结束,回调销毁,dom对象也销毁了