前端性能优化-页面加载渲染优化

参考资料:前端总结--性能优化
参考资料:js和css的加载形成阻塞化
参考资料:浏览器的工做原理
参考资料:css 阻塞 js阻塞
参考资料:分布渲染
参考资料:css加载会形成阻塞吗
参考资料:干货 | 十分钟读懂浏览器渲染流程javascript

1 知识体系

1.1 从URL输入到页面加载


首先咱们须要经过 DNS(域名解析系统)将 URL 解析为对应的 IP 地址,而后与这个 IP 地址肯定的那台服务器创建起 TCP 网络链接,随后咱们向服务端抛出咱们的 HTTP 请求,服务端处理完咱们的请求以后,把目标数据放在 HTTP 响应里返回给客户端,拿到响应数据的浏览器就能够开始走一个渲染的流程。渲染完毕,页面便呈现给了用户 css

将这个过程切分为以下的过程片断

  1. DNS 解析
  2. TCP 链接
  3. HTTP 请求抛出
  4. 服务端处理请求,HTTP 响应返回
  5. 浏览器拿到响应数据,解析响应内容,把解析的结果展现给用户

1.2 性能优化思惟导图


2 网路篇(http)

输入 URL 到显示页面这个过程当中,涉及到网络层面的,有三个主要过程:html

  1. DNS 解析
  2. TCP 链接
  3. HTTP 请求/响应

2.1 DNS解析


通常来讲,在前端优化中与 DNS 有关的有两点:前端

  1. 是减小DNS的请求次数,
  2. 是进行DNS预获取——DNS Prefetch

DNS 做为互联网的基础协议,其解析的速度彷佛很容易被网站优化人员忽视。如今大多数新浏览器已经针对DNS解析进行了优化,典型的一次DNS解析须要耗费20-120 毫秒,减小DNS解析时间和次数是个很好的优化方式。vue

DNS Prefetching 是让具备此属性的域名不须要用户点击连接就在后台解析,而域名解析和内容载入是串行的网络操做,因此这个方式能减小用户的等待时间,提高用户体验 。html5

默认状况下浏览器会对页面中和当前域名(正在浏览网页的域名)不在同一个域的域名进行预获取,而且缓存结果,这就是隐式的 DNS Prefetch。若是想对页面中没有出现的域进行预获取,那么就要使用显示 DNS Prefetch 了。java

DNS预解析具体用法webpack

//用meta信息来告知浏览器, 当前页面要作DNS预解析
<meta http-equiv="x-dns-prefetch-control" content="on">
//在页面header中使用link标签来强制对DNS预解析: 
<link rel="dns-prefetch" href="//www.zhix.net"> 
复制代码

注意:dns-prefetch需慎用,多页面重复DNS预解析会增长重复DNS查询次数,由于有开发者指出 禁用DNS 预读取能节省每个月100亿的DNS查询 。nginx

//若是须要禁止隐式的 DNS Prefetch
<meta http-equiv="x-dns-prefetch-control" content="off">
复制代码

2.2 HTTP


对于 DNS 解析和 TCP 链接两个步骤,咱们前端能够作的努力很是有限。相比之下,HTTP 链接这一层面的优化才是咱们网络优化的核心git

HTTP 优化有两个大的方向

  1. 减小请求次数
  2. 减小单次请求所花费的时间

2.2.1减小HTTP请求次数

  • 图片:雪碧图,图标字体文件
  1. 雪碧图
    多张小图片合并为一张图,利用CSS -background-position调整图片显示位置

  2. 图标字体文件
    阿里图标

  • 合并JS和CSS文件
  1. webpack
  2. gulp
  3. grunt
  • 浏览器缓存
    若是图片或者脚本,样式文件内容比较固定,不常常被修改,那么,尽量利用缓存技术,减小HTTP请求次数或文件下载次数
  1. 强缓存
  2. 协商缓存

2.2.2减小单次请求所花费的时间

3.网络篇(图片优化)

3.1不一样业务场景下的图片方案选型


前置知识:二进制位数与色彩的关系 在计算机中,像素用二进制数来表示。不一样的图片格式中像素与二进制位数之间的对应关系是不一样的。一个像素对应的二进制位数越多,它能够表示的颜色种类就越多,成像效果也就越细腻,文件体积相应也会越大。

3.2 JPEG/JPG


关键字:有损压缩、体积小、加载快、不支持透明

  • JPG 的优势

    JPG 最大的特色是有损压缩。这种高效的压缩算法使它成为了一种很是轻巧的图片格式。另外一方面,即便被称为“有损”压缩,JPG的压缩方式仍然是一种高质量的压缩方式:当咱们把图片体积压缩至原有体积的 50% 如下时,JPG 仍然能够保持住 60% 的品质。此外,JPG 格式以 24 位存储单个图,能够呈现多达 1600 万种颜色,足以应对大多数场景下对色彩的要求,这一点决定了它压缩先后的质量损耗并不容易被咱们人类的肉眼所察觉——前提是你用对了业务场景。

  • 使用场景

    JPG 适用于呈现色彩丰富的图片,在咱们平常开发中,JPG 图片常常做为大的背景图、轮播图或 Banner 图出现。

    两大电商网站对大图的处理,是 JPG 图片应用场景的最佳写照: 打开淘宝首页,咱们能够发现页面中最醒目、最庞大的图片,必定是以 .jpg 为后缀的: 使用 JPG 呈现大图,既能够保住图片的质量,又不会带来使人头疼的图片体积,是当下比较推崇的一种方案。

  • JPG 的缺陷

    有损压缩在上文所展现的轮播图上确实很难露出马脚,但当它处理矢量图形和 Logo 等线条感较强、颜色对比强烈的图像时,人为压缩致使的图片模糊会至关明显。此外,JPEG 图像不支持透明度处理,透明图片须要召唤 PNG 来呈现。

3.3 png


关键字:无损压缩、质量高、体积大、支持透明

  • PNG 的优势

    PNG(可移植网络图形格式)是一种无损压缩的高保真的图片格式。8 和 24,这里都是二进制数的位数。按照咱们前置知识里提到的对应关系,8 位的 PNG 最多支持 256 种颜色,而 24 位的能够呈现约 1600 万种颜色。

    PNG 图片具备比 JPG 更强的色彩表现力,对线条的处理更加细腻,对透明度有良好的支持。它弥补了上文咱们提到的 JPG 的局限性,惟一的 BUG 就是体积太大。

  • PNG-8 与 PNG-24 的选择题

    何时用 PNG-8,何时用 PNG-24,这是一个问题

    理论上来讲,当你追求最佳的显示效果、而且不在乎文件体积大小时,是推荐使用 PNG-24 的。

    但实践当中,为了规避体积的问题,咱们通常不用PNG去处理较复杂的图像。当咱们遇到适合 PNG 的场景时,也会优先选择更为小巧的 PNG-8。

    如何肯定一张图片是该用 PNG-8 仍是 PNG-24去呈现呢?好的作法是把图片先按照这两种格式分别输出,看 PNG-8输出的结果是否会带来肉眼可见的质量损耗,而且确认这种损耗是否在咱们(尤为是你的 UI 设计师)可接受的范围内,基于对比的结果去作判断。

  • 应用场景

    前面咱们提到,复杂的、色彩层次丰富的图片,用 PNG 来处理的话,成本会比较高,咱们通常会交给 JPG 去存储。

    考虑到 PNG 在处理线条和颜色对比度方面的优点,咱们主要用它来呈现小的 Logo、颜色简单且对比强烈的图片或背景等。

    此时咱们再次把目光转向性能方面堪称业界楷模的淘宝首页,咱们会发现它页面上的 Logo,不管大小,还真的都是 PNG 格式:

3.4 SVG


关键字:文本文件、体积小、不失真、兼容性好

  • SVG 的使用方式与应用场景

    将 SVG 写入 HTML

    将 SVG 写入独立文件后引入 HTML将 SVG 写入 HTML

3.5 Base64


关键字:文本文件、依赖编码、小图标解决方案

  • Base64 的应用场景

    图片的实际尺寸很小(你们能够观察一下掘金页面的 Base64 图,几乎没有超过 2kb 的)

    图片没法以雪碧图的形式与其它小图结合(合成雪碧图还是主要的减小 HTTP 请求的途径,Base64 是雪碧图的补充)

    图片的更新频率很是低(不需咱们重复编码和修改文件内容,维护成本较低)

3.6 WebP


关键字:年轻的全能型选手 是 Google 专为 Web 开发的一种旨在加快图片加载速度的图片格式,它支持有损压缩和无损压缩。

  • WebP 的优势

    WebP 像 JPEG 同样对细节丰富的图片信手拈来,像 PNG 同样支持透明,像 GIF 同样能够显示动态图片——它集多种图片文件格式的优势于一身。

  • WebP 的局限性

    兼容性

3.7 总结


不一样业务场景下的图片方案选型

4.存储篇(浏览器缓存)

4.1 什么是缓存


对于一个数据请求来讲,能够分为发起网络请求、后端处理、浏览器响应三个步骤

浏览器缓存能够帮助咱们在第一和第三步骤中优化性能。好比说直接使用缓存而不发起请求,或者发起了请求但后端存储的数据和前端一致,那么就没有必要再将数据回传回来,这样就减小了响应数据。

缓存思惟导图

4.2 缓存位置


4.2.1 缓存优先级

从缓存位置上来讲分为四种,而且各自有优先级,当依次查找缓存且都没有命中的时候,才会去请求网络。

Service Worker
Memory Cache
Disk Cache
Push Cache

4.2.2 Service Worker

不了解 MDN

4.2.3 MemoryCache

MemoryCache,是指存在内存中的缓存。从优先级上来讲,它是浏览器最早尝试去命中的一种缓存。从效率上来讲,它是响应速度最快的一种缓存。

内存缓存是快的,也是“短命”的。它和渲染进程“生死相依”,当进程结束后,也就是 tab 关闭之后,内存里的数据也将不复存在。

那么哪些文件会被放入内存呢?

事实上,这个划分规则,一直以来是没有定论的。不过想一想也能够理解,内存是有限的,不少时候须要先考虑即时呈现的内存余量,再根据具体的状况决定分配给内存和磁盘的资源量的比重——资源存放的位置具备必定的随机性

虽然划分规则没有定论,但根据平常开发中观察的结果,包括咱们开篇给你们展现的 Network 截图,咱们至少能够总结出这样的规律:资源存不存内存,浏览器秉承的是“节约原则”。咱们发现,Base64 格式的图片,几乎永远能够被塞进 memory cache,这能够视做浏览器为节省渲染开销的“自保行为”;此外,体积不大的 JS、CSS 文件,也有较大地被写入内存的概率——相比之下,较大的 JS、CSS 文件就没有这个待遇了,内存资源是有限的,它们每每被直接甩进磁盘。

4.2.4 Disk Cache

Disk Cache 也就是存储在硬盘中的缓存,读取速度慢点,可是什么都能存储到磁盘中,比之 Memory Cache 胜在容量和存储时效性上。

在全部浏览器缓存中,Disk Cache 覆盖面基本是最大的。它会根据 HTTP Herder 中的字段判断哪些资源须要缓存,哪些资源能够不请求直接使用,哪些资源已通过期须要从新请求。而且即便在跨站点的状况下,相同地址的资源一旦被硬盘缓存下来,就不会再次去请求数据。绝大部分的缓存都来自 Disk Cache,关于 HTTP 的协议头中的缓存字段,咱们会在下文进行详细介绍

浏览器会把哪些文件丢进内存中?哪些丢进硬盘中 对于大文件来讲,大几率是不存储在内存中的,反之优先 当前系统内存使用率高的话,文件优先存储进硬盘

4.2.5 Push Cache

不了解

push Cache(推送缓存)是 HTTP/2 中的内容,当以上三种缓存都没有命中时,它才会被使用

4.3 缓存过程分析


浏览器与服务器通讯的方式为应答模式,便是:浏览器发起HTTP请求 – 服务器响应该请求,那么浏览器怎么肯定一个资源该不应缓存,如何去缓存呢?浏览器第一次向服务器发起该请求后拿到请求结果后,将请求结果和缓存标识存入浏览器缓存,浏览器对于缓存的处理是根据第一次请求资源时返回的响应头来肯定的。具体过程以下图:

上图咱们能够知道

  1. 浏览器每次发起请求,都会先在浏览器缓存中查找该请求的结果以及缓存标识
  2. 浏览器每次拿到返回的请求结果都会将该结果和缓存标识存入浏览器缓存中

4.4 http缓存

HTTP 缓存是咱们平常开发中最为熟悉的一种缓存机制。它又分为强缓存和协商缓存。优先级较高的是强缓存,在命中强缓存失败的状况下,才会走协商缓存。

4.5 强缓存

强缓存:不会向服务器发送请求,直接从缓存中读取资源,在chrome控制台的Network选项中能够看到该请求返回200的状态码,而且Size显示from disk cache或from memory cache。强缓存能够经过设置两种 HTTP Header 实现:Expires 和 Cache-Control。

4.5.1 Expires

缓存过时时间,用来指定资源到期的时间,是服务器端的具体的时间点。也就是说,Expires=max-age + 请求时间,须要和Last-modified结合使用。Expires是Web服务器响应消息头字段,在响应http请求时告诉浏览器在过时时间前浏览器能够直接从浏览器缓存取数据,而无需再次请求。

Expires 是 HTTP/1 的产物,受限于本地时间,若是修改了本地时间,可能会形成缓存失效。Expires: Wed, 22 Oct 2018 08:41:00 GMT表示资源会在 Wed, 22 Oct 2018 08:41:00 GMT 后过时,须要再次请求。

  1. 缓存过时时间,用来指定资源的到期时间,是服务器端的具体的时间点
  2. 告诉浏览器在过时时间前浏览器能够直接从浏览器缓存取数据,而不用再次请求
  3. max-age的优化级高于expires,当有max-age的时候,会无视expires
  4. 当在有效时间内,若是服务器端的文件已经发生改变,可是浏览器端没法感知

4.5.2 Cache-Control

在HTTP/1.1中,Cache-Control是最重要的规则,主要用于控制网页缓存Cache-Control 能够在请求头或者响应头中设置,而且能够组合使用多种指令

  1. max-age
  2. s-maxage
  3. private
  4. public
  5. no-cache
  6. no-store

  • max-age

    max-age=xxx (xxx is numeric)表示缓存内容将在xxx秒后失效

    1. 设置缓存存储的最大周期,超过这个时间缓存被认为过时(单位秒)。与Expires相反,时间是相对于请求的时间,
    2. 优先级高于Expires

  • s-maxage

    1. 覆盖max-age 或者 Expires头,可是仅适用于共享缓存(好比各个代理),而且私有缓存2中它被忽略
    2. 能用于public,如CDN
    3. 优先级高于max-age

  • private

    全部内容只有客户端能够缓存

    表示中间节点不容许缓存,对于Browser <-- proxy1 <-- proxy2 <-- Server,proxy 会老老实实把Server 返回的数据发送给proxy1,本身不缓存任何数据。当下次Browser再次请求时proxy会作好请求转发而不是自做主张给本身缓存的数据

    1. 代表响应只能被单个用户缓存,不能做为共享缓存(即代理服务器不能缓存它),能够缓存响应内容
    2. 本身的服务器
  • public

    全部内容都将被缓存(客户端和代理服务器均可缓存)

    具体来讲响应可被任何中间节点缓存,如 Browser <-- proxy1 <-- proxy2 <-- Server,中间的proxy能够缓存资源,好比下次再请求同一资源proxy1直接把本身缓存的东西给 Browser 而再也不向proxy2要。

  • no-store

    全部内容都不会被缓存,即不使用强制缓存,也不使用协商缓存

    1. 缓存不该存储有关客户端请求或服务器响应的任何内容。
    2. 不会使用任何缓存策略
  • no-cache

    客户端缓存内容,是否使用缓存则须要通过协商缓存来验证决定。表示不使用 Cache-Control的缓存控制方式作前置验证,而是使用 Etag 或者Last-Modified字段来控制缓存。须要注意的是,no-cache这个名字有一点误导。设置了no-cache以后,并非说浏览器就再也不缓存数据,只是浏览器在使用缓存数据时,须要先确认一下数据是否还跟服务器保持一致

    1. 释放缓存副本以前,强制高速缓存将请求提交给原始服务器进行验证
    2. 这个文件无论怎么样,都会向服务器发起请求,去服务器哪边询问,这个文件有没有在缓存策略里

4.5.3 强缓存思惟导图

强缓存思惟导图

4.6 协商缓存


4.6.1 什么是协商缓存

协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,主要有如下两种状况:

协商缓存生效,返回304和Not Modified

协商缓存失效,返回200和请求结果

4.6.2 Last-Modified和If-Modified-Since

Last-Modified 是一个响应首部,其中包含源头服务器认定的资源作出修改的日期及时间。 它一般被用做一个验证器来判断接收到的或者存储的资源是否彼此一致。因为精确度比 ETag 要低,因此这是一个备用机制。包含有 If-Modified-Since 或 If-Unmodified-Since 首部的条件请求会使用这个字段。

  1. 基于客户端和服务端协商的缓存机制
  2. Last-Modified ----response header
  3. If-Modified-Since----request header
  4. 须要与cache-control共同使用
  5. max-age的优先级高于Last-Modified
  • 缺点
  1. 某些服务端不能获取精确的修改时间
  2. 文件修改时间改了,但文件内容却没有变

4.6.3 Etag/If-None-Match

ETagHTTP响应头是资源的特定版本的标识符。这可让缓存更高效,并节省带宽,由于若是内容没有改变,Web服务器不须要发送完整的响应。而若是内容发生了变化,使用ETag有助于防止资源的同时更新相互覆盖(“空中碰撞”)

  1. 文件内容的hash值
  2. etag--response header
  3. if-none-match -- request header
  4. 要与cache-control共同使用

4.6.4二者对比

  1. 首先在精确度上,Etag要优于Last-Modified。
  2. 第二在性能上,Etag要逊于Last-Modified,毕竟Last-Modified只须要记录时间,而Etag须要服务器经过算法来计算出一个hash值。
  3. 第三在优先级上,服务器校验优先考虑Etag

4.6.5 思惟导图

4.7 参考


浏览器缓存机制介绍与缓存策略剖析
一文读懂前端缓存
深刻理解浏览器的缓存机制

5. 存储篇(本地存储)

5.1 Cookie


  1. Cookie 的本职工做并不是本地存储,而是“维持状态”。
  2. Cookie 不够大
  3. 同一个域名下的全部请求,都会携带 Cookie

5.2 Local Storage,Session Storage


5.2.1 概述

存储容量大: Web Storage 根据浏览器的不一样,存储容量能够达到 5-10M 之间 仅位于浏览器端,不与服务端发生通讯。

5.2.2 Local Storage 与 Session Storage 的区别

  • 生命周期: Local Storage 是持久化的本地存储,存储在其中的数据是永远不会过时的,使其消失的惟一办法是手动删除;而 Session Storage 是临时性的本地存储,它是会话级别的存储,当会话结束(页面被关闭)时,存储内容也随之被释放。

  • 做用域: Local Storage、Session Storage 和 Cookie 都遵循同源策略。但 Session Storage 特别的一点在于,即使是相同域名下的两个页面,只要它们不在同一个浏览器窗口中打开,那么它们的 Session Storage 内容便没法共享。

5.2.3 应用场景

  • Local Storage

    1. 理论上 Cookie 没法胜任的、能够用简单的键值对来存取的数据存储任务,均可以交给 Local Storage 来作。
    2. 存储一些内容稳定的资源。好比图片内容丰富的电商网站会用它来存储 Base64 格式的图片字符串
    3. 存储一些不常常更新的 CSS、JS 等静态资源
  • Session Storage

    微博的 Session Storage 就主要是存储你本次会话的浏览足迹

5.3 IndexedDB


  1. IndexedDB 是没有存储上限的(通常来讲不会小于 250M)
  2. IndexedDB 能够看作是 LocalStorage 的一个升级,当数据的复杂度和规模上升到了 LocalStorage 没法解决的程度,咱们毫无疑问能够请出 IndexedDB 来帮忙。

6. CDN

6.1 什么是CDN


CDN (Content Delivery Network,即内容分发网络)指的是一组分布在各个地区的服务器。这些服务器存储着数据的副本,所以服务器能够根据哪些服务器与用户距离最近,来知足数据的请求。 CDN 提供快速服务,较少受高流量影响。

6.2 为何要用 CDN


缓存、本地存储带来的性能提高,是否是只能在“获取到资源并把它们存起来”这件事情发生以后?也就是说,首次请求资源的时候,这些招数都是救不了咱们的。要提高首次请求的响应能力,咱们还须要借助 CDN 的能力

6.3 CDN 如何工做


假设个人根服务器在杭州,同时在图示的五个城市里都有本身可用的机房

此时有一位北京的用户向我请求资源。在网络带宽小、用户访问量大的状况下,杭州的这一台服务器或许不那么给力,不能给用户很是快的响应速度。因而我灵机一动,把这批资源 copy 了一批放在北京的机房里。当用户请求资源时,就近请求北京的服务器,北京这台服务器低头一看,这个资源我存了,离得这么近,响应速度确定噌噌的!那若是北京这台服务器没有 copy 这批资源呢?它会再向杭州的根服务器去要这个资源。在这个过程当中,北京这台服务器就扮演着 CDN 的角色。

6.4 CDN的核心功能


CDN 的核心点有两个,一个是缓存,一个是回源。

  • 缓存

    “缓存”就是说咱们把资源 copy 一份到 CDN 服务器上这个过程

  • 回源

    就是说 CDN 发现本身没有这个资源(通常是缓存的数据过时了),转头向根服务器(或者它的上层服务器)去要这个资源的过程。

6.5 CDN 与前端性能优化


CDN 每每被用来存放静态资源

“根服务器”本质上是业务服务器,它的核心任务在于生成动态页面或返回非纯静态页面,这两种过程都是须要计算的。业务服务器仿佛一个车间,车间里运转的机器轰鸣着为咱们产出所需的资源;相比之下,CDN 服务器则像一个仓库,它只充当资源的“栖息地”和“搬运工”。

所谓“静态资源”,就是像 JS、CSS、图片等不须要业务服务器进行计算即得的资源。而“动态资源”,顾名思义是须要后端实时动态生成的资源,较为常见的就是 JSP、ASP 或者依赖服务端渲染获得的 HTML 页面。

什么是“非纯静态资源”呢?它是指须要服务器在页面以外做额外计算的 HTML 页面。具体来讲,当我打开某一网站以前,该网站须要经过权限认证等一系列手段确认个人身份、进而决定是否要把 HTML 页面呈现给我。这种状况下 HTML 确实是静态的,但它和业务服务器的操做耦合,咱们把它丢到CDN 上显然是不合适的。

6.6 CDN 的实际应用


静态资源自己具备访问频率高、承接流量大的特色,所以静态资源加载速度始终是前端性能的一个很是关键的指标。CDN 是静态资源提速的重要手段,在许多一线的互联网公司,“静态资源走 CDN”并非一个建议,而是一个规定

www.taobao.com/

能够看到业务服务器确实是返回给了咱们一个还没有被静态资源加持过的简单 HTML 页面

随便点开一个静态资源,能够看到它都是从 CDN 服务器上请求来的

6.7 cdn与cookie


Cookie 是紧跟域名的。同一个域名下的全部请求,都会携带 Cookie。你们试想,若是咱们此刻仅仅是请求一张图片或者一个 CSS 文件,咱们也要携带一个 Cookie 跑来跑去(关键是 Cookie 里存储的信息我如今并不须要),这是一件多么劳民伤财的事情。Cookie 虽然小,请求却能够有不少,随着请求的叠加,这样的没必要要的 Cookie 带来的开销将是没法想象的

同一个域名下的请求会不分青红皂白地携带 Cookie,而静态资源每每并不须要 Cookie 携带什么认证信息。把静态资源和主页面置于不一样的域名下,完美地避免了没必要要的 Cookie 的出现!

看起来是一个不起眼的小细节,但带来的效用倒是惊人的。以电商网站静态资源的流量之庞大,若是没把这个多余的 Cookie 拿下来,不只用户体验会大打折扣,每一年因性能浪费带来的经济开销也将是一个很是恐怖的数字。

7.渲染篇(服务端渲染)

7.1 客户端渲染


客户端渲染模式下,服务端会把渲染须要的静态文件发送给客户端,客户端加载过来以后,本身在浏览器里跑一遍 JS,根据 JS 的运行结果,生成相应的 DOM

<!doctype html>
<html>
  <head>
    <title>我是客户端渲染的页面</title>
  </head>
  <body>
    <div id='root'></div>
    <script src='index.js'></script>
  </body>
</html>
复制代码

根节点下究竟是什么内容呢?你不知道,我不知道,只有浏览器把 index.js 跑过一遍后才知道,这就是典型的客户端渲染。

页面上呈现的内容,你在 html 源文件里里找不到——这正是它的特色。

7.2 服务端渲染


服务端渲染的模式下,当用户第一次请求页面时,由服务器把须要的组件或页面渲染成 HTML 字符串,而后把它返回给客户端。客户端拿到手的,是能够直接渲染而后呈现给用户的 HTML 内容,不须要为了生成 DOM 内容本身再去跑一遍 JS 代码。

使用服务端渲染的网站,能够说是“所见即所得”,页面上呈现的内容,咱们在 html 源文件里也能找到

7.3 服务端渲染解决了什么性能问题


事实上,不少网站是出于效益(seo)的考虑才启用服务端渲染,性能却是在其次。

假设 A 网站页面中有一个关键字叫“前端性能优化”,这个关键字是 JS 代码跑过一遍后添加到 HTML 页面中的。那么客户端渲染模式下,咱们在搜索引擎搜索这个关键字,是找不到 A 网站的——搜索引擎只会查找现成的内容,不会帮你跑 JS 代码。A 网站的运营方见此情形,感到很头大:搜索引擎搜不出来,用户找不到咱们,谁还会用个人网站呢?为了把“现成的内容”拿给搜索引擎看,A 网站不得不启用服务端渲染。

但性能在其次,不表明性能不重要。服务端渲染解决了一个很是关键的性能问题——首屏加载速度过慢。在客户端渲染模式下,咱们除了加载 HTML,还要等渲染所需的这部分 JS 加载完,以后还得把这部分 JS 在浏览器上再跑一遍。这一切都是发生在用户点击了咱们的连接以后的事情,在这个过程结束以前,用户始终见不到咱们网页的庐山真面目,也就是说用户一直在等!相比之下,服务端渲染模式下,服务器给到客户端的已是一个直接能够拿来呈现给用户的网页,中间环节早在服务端就帮咱们作掉了,用户岂不“美滋滋”?。

7.4 服务端渲染的应用场景


服务端渲染本质上是本该浏览器作的事情,分担给服务器去作。这样当资源抵达浏览器时,它呈现的速度就快了

但仔细想一想,在这个网民遍地的时代,几乎有多少个用户就有多少台浏览器。用户拥有的浏览器总量多到数不清,那么一个公司的服务器又有多少台呢?咱们把这么多台浏览器的渲染压力集中起来,分散给相比之下数量并很少的服务器,服务器确定是承受不住的

除非网页对性能要求过高了,以致于全部的招式都用完了,性能表现仍是不尽人意,这时候咱们就能够考虑向老板多申请几台服务器,把服务端渲染搞起来了~

8.渲染篇(浏览器渲染)

8.1 浏览器内核


浏览器内核能够分红两部分:渲染引擎(Layout Engine 或者 Rendering Engine)和 JS 引擎

渲染引擎又包括了 HTML 解释器、CSS 解释器、布局、网络、存储、图形、音视频、图片解码器等等零部件。

8.2 浏览器渲染过程解析


8.3 基于渲染流程的 CSS 优化建议


8.3.1 CSS 选择符是从右到左进行匹配的

#myList li {}
复制代码

浏览器必须遍历页面上每一个 li 元素,而且每次都要去确认这个 li 元素的父元素 id 是否是 myList

8.3.2 具体优化

  1. 避免使用通配符,只对须要用到的元素进行选择
  2. 关注能够经过继承实现的属性,避免重复匹配重复定义。
  3. 少用标签选择器。若是能够,用类选择器替代
  4. 不要多此一举,id 和 class 选择器不该该被多余的标签选择器拖后腿
  5. 减小嵌套。后代选择器的开销是最高的,所以咱们应该尽可能将选择器的深度降到最低(最高不要超过三层),尽量使用类来关联每个标签元素

8.4 告别阻塞:CSS 与 JS 的加载顺序优化


HTML、CSS 和 JS,都具备阻塞渲染的特性。 HTML 阻塞,天经地义——没有 HTML,何来 DOM?没有 DOM,渲染和优化,都是空谈。

8.4.1 CSS 的阻塞

在刚刚的过程当中,咱们提到 DOM 和 CSSOM 协力才能构建渲染树。这一点会给性能形成严重影响:默认状况下,CSS 是阻塞的资源。浏览器在构建 CSSOM 的过程当中,不会渲染任何已处理的内容。即使 DOM 已经解析完毕了,只要 CSSOM 不 OK,那么渲染这个事情就不 OK(这主要是为了不没有 CSS 的 HTML 页面丑陋地“裸奔”在用户眼前)。

只有当咱们开始解析 HTML 后、解析到 link 标签或者 style 标签时,CSS 才登场,CSSOM 的构建才开始。 不少时候,DOM 不得不等待 CSSOM。所以咱们能够这样总结:

CSS 是阻塞渲染的资源。须要将它尽早、尽快地下载到客户端,以便缩短首次渲染的时间。
尽早(将 CSS 放在 head 标签里)和尽快(启用 CDN 实现静态资源加载速度的优化)

  1. css文件是并行下载的
  2. css的下载会阻塞后面js的执行
  3. css的下载不会阻塞后面的html的解析,可是会阻塞dom渲染
  4. css的下载会阻塞后面的DOM的onload事件。
  5. css的下载不会阻塞后面js的下载,可是js下载完成后,被阻塞执行。

8.4.2 JS 的阻塞

JS 的做用在于修改,它帮助咱们修改网页的方方面面:内容、样式以及它如何响应用户交互。这“方方面面”的修改,本质上都是对 DOM 和 CSSDOM 进行修改。所以 JS 的执行会阻止 CSSOM,在咱们不做显式声明的状况下,它也会阻塞 DOM。

JS 引擎是独立于渲染引擎存在的。咱们的 JS 代码在文档的何处插入,就在何处执行。当 HTML 解析器遇到一个 script 标签时,它会暂停渲染过程,将控制权交给 JS 引擎。JS 引擎对内联的 JS 代码会直接执行,对外部 JS 文件还要先获取到脚本、再进行执行。等 JS 引擎运行完毕,浏览器又会把控制权还给渲染引擎,继续 CSSOM 和 DOM 的构建。 所以与其说是 JS 把 CSS 和 HTML 阻塞了,不如说是 JS 引擎抢走了渲染引擎的控制权。

  1. 现代浏览器会并行加载js文件。

  2. 加载或者执行js时会阻塞对标签的解析,也就是阻塞了dom树的造成,只有等到js执行完毕,浏览器才会继续解析标签。没有dom树,浏览器就没法渲染,因此当加载很大的js文件时,能够看到页面很长时间是一片空白

之因此会阻塞对标签的解析是由于加载的js中可能会建立,删除节点等,这些操做会对dom树产生影响,若是不阻塞,等浏览器解析完标签生成dom树后,js修改了某些节点,那么浏览器又得从新解析,而后生成dom树,性能比较差

8.4.3 JS的三种加载方式

正常模式

这种状况下 JS 会阻塞浏览器,浏览器必须等待 index.js 加载和执行完毕才能去作其它事情。

<script src="index.js"></script>
复制代码

async(异步) 模式

async 模式下,JS 不会阻塞浏览器作任何其它的事情。它的加载是异步的,当它加载结束,JS 脚本会当即执行。

<script async src="index.js"></script>
复制代码

defer(延缓) 模式

efer 模式下,JS 的加载是异步的,执行是被推迟的。等整个文档解析完成、DOMContentLoaded 事件即将被触发时,被标记了 defer 的 JS 文件才会开始依次执行。

<script defer src="index.js"></script>
复制代码

从应用的角度来讲,通常当咱们的脚本与 DOM 元素和其它脚本之间的依赖关系不强时,咱们会选用 async;当脚本依赖于 DOM 元素和其它脚本的执行结果时,咱们会选用 defer。

9. 渲染篇(dom优化)

10. 渲染篇(Event Loop与异步更新策略(vue))

10.1 什么是异步更新?


当咱们使用 Vue 或 React 提供的接口去更新数据时,这个更新并不会当即生效,而是会被推入到一个队列里。待到适当的时机,队列中的更新任务会被批量触发。这就是异步更新。

异步更新能够帮助咱们避免过分渲染,是咱们上节提到的“让 JS 为 DOM 分压”的典范之一。

10.2 异步更新的优越性


异步更新的特性在于它只看结果,所以渲染引擎不须要为过程买单

有时咱们会遇到这样的状况

// 任务一
this.content = '第一次测试'
// 任务二
this.content = '第二次测试'
// 任务三
this.content = '第三次测试'
复制代码

咱们在三个更新任务中对同一个状态修改了三次,若是咱们采起传统的同步更新策略,那么就要操做三次 DOM。但本质上须要呈现给用户的目标内容其实只是第三次的结果,也就是说只有第三次的操做是有意义的——咱们白白浪费了两次计算。

但若是咱们把这三个任务塞进异步更新队列里,它们会先在 JS 的层面上被批量执行完毕。当流程走到渲染这一步时,它仅仅须要针对有意义的计算结果操做一次 DOM——这就是异步更新的妙处。

10.3 Vue状态更新手法:nextTick


Vue在观察到数据变化时并非直接更新DOM,而是开启一个队列,并缓冲在同一事件循环中发生的全部数据改变。在缓冲时会去除重复数据,从而避免没必要要的计算和DOM操做。而后,在下一个事件循环tick中,Vue刷新队列并执行实际工做

$nextTick就是用来知道何时DOM更新完成的

11. 渲染篇(回流与重绘)

11.1 reflow与repaint


根据渲染树布局,计算CSS样式,即每一个节点在页面中的大小和位置等几何信息。HTML默认是流式布局的,CSS和js会打破这种布局,改变DOM的外观样式以及大小和位置。这时就要提到两个重要概念:replaint和reflow。

replaint:屏幕的一部分重画,不影响总体布局,好比某个CSS的背景色变了,但元素的几何尺寸和位置不变。

reflow: 意味着元件的几何尺寸变了,咱们须要从新验证并计算渲染树。是渲染树的一部分或所有发生了变化。这就是Reflow,或是Layout。

因此咱们应该尽可能减小reflow和replaint,我想这也是为何如今不多有用table布局的缘由之一。

display:none 会触发 reflow,visibility: hidden属性并不算是不可见属性,它的语义是隐藏元素,但元素仍然占据着布局空间,它会被渲染成一个空框,因此visibility:hidden 只会触发 repaint,由于没有发生位置变化。

有些状况下,好比修改了元素的样式,浏览器并不会马上 reflow 或 repaint 一次,而是会把这样的操做积攒一批,而后作一次 reflow,这又叫异步 reflow 或增量异步 reflow。

有些状况下,好比 resize 窗口,改变了页面默认的字体等。对于这些操做,浏览器会立刻进行 reflow。

12.2 节流与防抖的本质


这两个东西都以闭包的形式存在。

它们经过对事件对应的回调函数进行包裹、以自由变量的形式缓存时间信息,最后用 setTimeout 来控制事件的触发频率。

12.3 节流


在某段时间内,无论你触发了多少次回调,我都只认第一次,并在计时结束时给予响应。

如今有一个旅客刚下了飞机,须要用车,因而打电话叫了该机场惟一的一辆机场大巴来接。司机开到机场,心想来都来了,多接几我的一块儿走吧,这样这趟才跑得值——我等个十分钟看看。因而司机一边打开了计时器,一边招呼后面的客人陆陆续续上车。在这十分钟内,后面下飞机的乘客都只能乘这一辆大巴,十分钟过去后,无论后面还有多少没挤上车的乘客,这班车都必须发走

12.4 防抖


在某段时间内,无论你触发了多少次回调,我都只认最后一次。

第一个乘客上车后,司机开始计时(好比说十分钟)。十分钟以内,若是又上来了一个乘客,司机会把计时器清零,从新开始等另外一个十分钟(延迟了等待)。直到有这么一位乘客,从他上车开始,后续十分钟都没有新乘客上车,司机会认为确实没有人须要搭这趟车了,才会把车开走。

12.5 防抖的缺憾


防抖 的问题在于它“太有耐心了”。试想,若是用户的操做十分频繁——他每次都不等 防抖 设置的 delay 时间结束就进行下一次操做,因而每次 防抖 都为该用户从新生成定时器,回调函数被延迟了不可胜数次。频繁的延迟会致使用户迟迟得不到响应,用户一样会产生“这个页面卡死了”的观感。 为了不弄巧成拙,咱们须要借力 节流 的思想,打造一个“有底线”的 防抖——等你能够,但我有个人原则:delay 时间内,我能够为你从新生成定时器;但只要delay的时间到了,我必需要给用户一个响应。这个 节流与 防抖 “合体”思路,已经被不少成熟的前端库应用到了它们的增强版 节流 函数的实现中

11 性能监控

performance

function getsec(time) {
  return time / 1000 + 's'
}
window.onload = function () {
  var per = window.performance.timing;
  console.log('DNS查询耗时' + getsec(per.domainLookupEnd - per.domainLookupStart))
  console.log('TCP连接耗时' + getsec(per.connectEnd - per.connectStart))
  console.log('request请求响应耗时' + getsec(per.responseEnd - per.responseStart))
  console.log('dom渲染耗时' + getsec(per.domComplete - per.domInteractive))
  console.log('白屏时间' + getsec(firstPaint - pageStartTime))
  console.log('domready可操做时间' + getsec(per.domContentLoadedEventEnd - per.fetchStart))
}
复制代码
相关文章
相关标签/搜索