图片优化
JPEG/JPG:有损压缩、体积小、加载快、不支持透明
**应用场景:**JPG 图片经常作为大的背景图、轮播图或 Banner 图出现。
**JPG 的缺陷:**处理矢量图形和 Logo 等线条感较强、颜色对比强烈的图像时,人为压缩导致的图片模糊会相当明显不支持透明度处理,透明图片需要 PNG 来呈现。
PNG-8 与 PNG-24:无损压缩、质量高、体积大、支持透明
**应用场景:**小的 Logo、颜色简单且对比强烈的图片或背景等
**PNG-8 与 PNG-24 的选择:**追求最佳的显示效果、并且不在意文件体积大小时,使用 PNG-24 ;图片先按照这两种格式分别输出,看 PNG-8 输出的结果是否会带来肉眼可见的质量损耗,并且确认这种损耗是否在我们(尤其是你的 UI 设计师)可接受的范围内,基于对比的结果去做判断。
SVG:文本文件、体积小、不失真、兼容性好
**应用场景:**图片质量要求高,体积小
**SVG的缺陷:**渲染成本比较高,对性能来说是很不利的;SVG 存在着其它图片格式所没有的学习成本(它是可编程的)
Base64:文本文件、依赖编码、小图标解决方案
**应用场景:**logo等小图标,满足以下条件:
**a.**图片的实际尺寸很小(大家可以观察一下掘金页面的 Base64 图,几乎没有超过 2kb 的)
**b.**图片无法以雪碧图的形式与其它小图结合(合成雪碧图仍是主要的减少 HTTP 请求的途径,Base64 是雪碧图的补充)
**c.**图片的更新频率非常低(不需我们重复编码和修改文件内容,维护成本较低)
WebP:体积小,质量高,支持动态图,支持透明,兼容性差
**应用场景:**浏览器只能是chrome
浏览器缓存机制介绍与缓存策略剖析
资源请求的优先级:
1.Memory Cache(from memory cache)
2.Service Worker Cache(from ServiceWorker,实现离线缓存、消息推送和网络代理)
3.HTTP Cache
4.Push Cache
**from memory cache:**从内存中拿到的,不会请求服务器已经加载过该资源且缓存在了内存当中,关闭该页面时被内存释放掉了,再次重新打开相同页面不会出现from memory cache
**from disk cache:**其实就是http缓存 通过http 协议来约定,由服务端来设置response header,此资源是从磁盘当中取出的,也是在已经在之前的某个时间加载过该资源,不会请求服务器但是此资源不会随着该页面的关闭而释放掉,因为是存在硬盘当中的,下次打开仍会from disk cache
**资源本身大小数值:**当http状态为200是实实在在从浏览器获取的资源,当http状态为304时该数字是与服务端通信报文的大小,并不是该资源本身的大小,该资源是从本地获取的
HTTP 缓存机制
HTTP 缓存分为强缓存和协商缓存。优先级较高的是强缓存,在命中强缓存失败的情况下,才会走协商缓存
强缓存的实现
从 expires 到 cache-control
**expires:**Response Headers 中将过期时间写入 expires 字段
expires: Wed, 11 Sep 2019 16:12:18 GMT
**缺点:**对“本地时间”的依赖。如果服务端和客户端的时间设置可能不同,或者我直接手动去把客户端的时间改掉,那么 expires 将无法达到我们的预期
**cache-control:**Response Headers 中将过期时间写入 cache-control 字段
cache-control: max-age=31536000
max-age 控制资源的有效期
cache-control: max-age=3600, s-maxage=31536000
s-maxage (s表示server)优先级高于 max-age,用于表示 cache 服务器上(比如 cache CDN)的缓存的有效时间的,并只对 public 缓存有效。仅在代理服务器中生效,客户端中我们只考虑max-age
Public可以被浏览器缓存,也可以被代理服务器缓存(不需要我们设置,设置了s-maxage就表示可以缓存)
Private只能被浏览器缓存(默认值)
no-cache 每一次发起请求都不会再去询问浏览器的缓存情况,而是直接向服务端去确认该资源是否过期(即走我们下文即将讲解的协商缓存的路线)。
no-store 不使用任何缓存策略。只允许直接向服务端发送请求、并下载完整的响应。
协商缓存
协商缓存: 浏览器向服务器询问缓存的相关信息,进而判断是重新发起请求、下载完整的响应,还是从本地获取缓存的资源。
协商缓存的实现
从 Last-Modified 到 Etag
Last-Modified 时间戳,如果我们启用了协商缓存,它会在首次请求时随着 Response Headers 返回:
Last-Modified: Fri, 27 Oct 2017 06:35:57 GMT
随后我们每次请求时,会带上一个 If-Modified-Since 的时间戳字段,它的值正是上一次 response 返回给它的 last-modified 值:
If-Modified-Since: Fri, 27 Oct 2017 06:35:57 GMT
服务器接比对该时间戳和资源在服务器上的最后修改时间是否一致,从而判断资源是否发生了变化。如果发生了变化,返回一个完整的响应内容,并在 Response Headers 中添加新的 Last-Modified 值;否则,返回304 响应,Response Headers 不会再添加 Last-Modified 字段。
缺点: 服务器有可能出现没有正确感知文件的变化(包括编辑了文件但是内容没变化,服务器会重新请求;修改时间小于1000ms,服务器没有重新请求)
Etag:当首次请求时,我们会在响应头里获取到一个最初的标识符字符串
ETag: W/"2a3b-1602480f459"
下一次请求时,请求头里就会带上一个值相同的、名为 if-None-Match 的字符串供服务端比对了:
If-None-Match: W/"2a3b-1602480f459"
**缺点:**Etag 的生成过程需要服务器额外付出开销,影响服务端的性能
Etag 不能替代 Last-Modified,只能作为 Last-Modified 的补充和强化存在。 Etag 在感知文件变化上比 Last-Modified 更加准确,优先级也更高。当 Etag 和 Last-Modified 同时存在时,以 Etag 为准。
HTTP 缓存决策
流程图解读:
当我们的资源内容不可复用时,直接为 Cache-Control 设置 no-store,拒绝一切形式的缓存;
否则考虑是否每次都需要向服务器进行缓存有效确认,如果需要,那么设 Cache-Control 的值为 no-cache;
否则考虑该资源是否可以被代理服务器缓存,根据其结果决定是设置为 private 还是 public;
然后考虑该资源的过期时间,设置对应的 max-age 和 s-maxage 值;
最后,配置协商缓存需要用到的 Etag、Last-Modified 等参数。
本地存储
cookie:附着在HTTP请求上,可以携带用户信息
性能劣势:只能用来存取少量的信息,内存4kb;过量的 Cookie 会带来巨大的性能浪费,同一个域名下的所有请求,都会携带 Cookie
Web Storage:
Web Storage 的特性:存储容量大,可以达到 5-10M 之间;仅位于浏览器端,不与服务端发生通信
Local Storage 与 Session Storage
区别:
生命周期:Local Storage 永远不会过期,使其消失的唯一办法是手动删除; Session Storage 会话结束(页面被关闭)时,存储内容也随之被释放。
作用域:Local Storage、Session Storage 和 Cookie 都遵循同源策略。但 Session Storage 特别的一点在于,即便是相同域名下的两个页面,只要它们不在同一个浏览器窗口中打开,那么它们的 Session Storage 内容便无法共享。
应用场景
Local Storage:存储一些内容稳定的资源。比如图片内容丰富的电商网站会用它来存储 Base64 格式的图片字符串,不经常用的css,js
Session Storage:只适用于当前会话,微博的 Session Storage 就主要是存储你本次会话的浏览足迹
CDN 的缓存与回源机制解析
**CDN **(Content Delivery Network,即内容分发网络)指的是一组分布在各个地区的服务器。这些服务器存储着数据的副本,因此服务器可以根据哪些服务器与用户距离最近,来满足数据的请求。 CDN 提供快速服务,较少受高流量影响。
**工作流程:**用户就近请求资源,如果资源存在,则快速响应,不存在则向其他服务器要资源,copy到自己服务器上。
CDN 的核心点有两个,一个是缓存,一个是回源:“缓存”就是说我们把资源 copy 一份到 CDN 服务器上这个过程,“回源”就是说 CDN 发现自己没有这个资源(一般是缓存的数据过期了),转头向根服务器(或者它的上层服务器)去要这个资源的过程。
CDN 与前端性能优化
存放静态资源,加载速度快。
涉及到 CDN 服务器本身的性能优化、CDN 节点的地址选取,前端方面,就是选取不一样的cdn域名,可避免传入不必要的cookie
服务端渲染的探索与实践
服务端渲染的运行机制
客户端渲染:客户端渲染模式下,服务端会把渲染需要的静态文件发送给客户端,客户端加载过来之后,自己在浏览器里跑一遍 JS,根据 JS 的运行结果,生成相应的 DOM。特点:页面上呈现的内容,你在 html 源文件里里找不到
服务端渲染:服务端渲染的模式下,当用户第一次请求页面时,由服务器把需要的组件或页面渲染成 HTML 字符串,然后把它返回给客户端。客户端拿到手的,是可以直接渲染然后呈现给用户的 HTML 内容,不需要为了生成 DOM 内容自己再去跑一遍 JS 代码。特点:页面上呈现的内容,我们在 html 源文件里也能找到。
服务端渲染解决问题:
1.seo:搜索引擎只查找现成的内容,服务端渲染可以把现成的内容给搜索引擎搜索到
2.性能优化:解决首屏加载过慢
服务端渲染的应用场景:渲染需要消耗服务器性能,用户拥有的浏览器总量多到数不清,那么一个公司的服务器有限,我们把这么多台浏览器的渲染压力集中起来,分散给相比之下数量并不多的服务器,服务器肯定是承受不住的。所以一般不使用
解锁浏览器背后的运行机制
浏览器内核由两部分组成:渲染引擎(又包括了 HTML 解释器、CSS 解释器、布局、网络、存储、图形、音视频、图片解码器等等零部件)和 JS 引擎
渲染引擎:调用浏览器各个零部件,将网页源代码转换为图像的过程.渲染引擎又包括了 HTML 解释器、CSS 解释器、布局、网络、存储、图形、音视频、图片解码器等等零部件
浏览器渲染过程解析:
解析 HTML 在这一步浏览器执行了所有的加载解析逻辑,在解析 HTML 的过程中发出了页面渲染所需的各种外部资源请求。
计算样式 浏览器将识别并加载所有的 CSS 样式信息与 DOM 树合并,最终生成页面 render 树(:after :before 这样的伪元素会在这个环节被构建到 DOM 树中)。
计算图层布局 页面中所有元素的相对位置信息,大小等信息均在这一步得到计算。
绘制图层 在这一步中浏览器会根据我们的 DOM 代码结果,把每一个页面图层转换为像素,并对所有的媒体文件进行解码。
整合图层,得到页面 最后一步浏览器会合并合各个图层,将数据由 CPU 输出给 GPU 最终绘制在屏幕上。(复杂的视图层会给这个阶段的 GPU 计算带来一些压力,在实际应用中为了优化动画性能,我们有时会手动区分不同的图层)。
渲染流程对应以下树
DOM 树:解析 HTML 以创建的是 DOM 树(DOM tree ):渲染引擎开始解析 HTML 文档,转换树中的标签到 DOM 节点,它被称为“内容树”。
CSSOM 树:解析 CSS(包括外部 CSS 文件和样式元素)创建的是 CSSOM 树。CSSOM 的解析过程与 DOM 的解析过程是并行的。
渲染树:CSSOM 与 DOM 结合,之后我们得到的就是渲染树(Render tree )。
布局渲染树:从根节点递归调用,计算每一个元素的大小、位置等,给每个节点所应该出现在屏幕上的精确坐标,我们便得到了基于渲染树的布局渲染树(Layout of the render tree)。
绘制渲染树: 遍历渲染树,每个节点将使用 UI 后端层来绘制。整个过程叫做绘制渲染树(Painting the render tree)
渲染短板:
之后每当一个新元素加入到这个 DOM 树当中,浏览器便会通过 CSS 引擎查遍 CSS 样式表,找到符合该元素的样式规则应用到这个元素上,然后再重新去绘制它
基于渲染短板的 CSS 优化建议
1.CSS 选择符是从右到左进行匹配的。CSS 引擎查找样式表,对每条规则都按从右到左的顺序去匹配。 看如下规则:
#myList li {}
a.避免使用通配符,只对需要用到的元素进行选择。
b.关注可以通过继承实现的属性,避免重复匹配重复定义。
c.少用标签选择器。
d.不要画蛇添足,id 和 class 选择器不应该被多余的标签选择器拖后腿
e.减少嵌套。后代选择器的开销是最高的,因此我们应该尽量将选择器的深度降到最低(最高不要超过三层),尽可能使用类来关联每一个标签元素
告别阻塞:CSS 与 JS 的加载顺序优化
CSS 的阻塞:浏览器在构建 CSSOM 的过程中,不会渲染任何已处理的内容。即便 DOM 已经解析完毕了,还是需要等待CSSOM全部构建完之后才做渲染(这主要是为了避免没有 CSS 的 HTML 页面丑陋地“裸奔”在用户眼前)。
优化点:
CSS 是阻塞渲染的资源。需要将它:
尽早(将 CSS 放在 head 标签里)和
尽快(启用 CDN 实现静态资源加载速度的优化)
地下载到客户端,以便缩短首次渲染的时间
JS 的阻塞:当 HTML 解析器遇到一个 script 标签时,它会暂停渲染过程,将控制权交给 JS 引擎。JS 引擎对内联的 JS 代码会直接执行,对外部 JS 文件还要先获取到脚本、再进行执行。等 JS 引擎运行完毕,浏览器又会把控制权还给渲染引擎,继续 CSSOM 和 DOM 的构建。 因此与其说是 JS 把 CSS 和 HTML 阻塞了,不如说是 JS 引擎抢走了渲染引擎的控制权。由于js并非第一时间需要渲染,所以可以有以下优化点
优化点:
异步:
<script async src="index.js"></script>
<script defer src="index.js"></script>
虽然都是异步,但是async会加载结束后立即执行,defer推迟执行
使用时机:一般当我们的脚本与 DOM 元素和其它脚本之间的依赖关系不强时,我们会选用 async;当脚本依赖于 DOM 元素和其它脚本的执行结果时,我们会选用 defer。