前端性能优化

规则1—— 减少HTTP请求

用户看到绚丽多彩的网页之前

  • 80% 的时间用于加载HTML文档所需的 图片、js、css、等资源进行的 http请求上。
  • 因此改善响应时间最简单的途径就是 减少 http请求数量。

图片地图

  • 上古时代~ 至少我没用过

CSS Sprites

  • 几乎现在没人用了—— 上古时代的 精灵图 or 雪碧图
  • 多个图片合并在一张大图上

内联图片

将图片转化为 base64码。这样就不会去请求资源了,

<img src="data:image/png;base64,iVAGRw0KGDCFGNSUhEUgACBBQAVGADCAIATYJ7ljmRGGAAGElEVQQIW2P4DwcMDAxAfBvMAhEQMYgcACEHG8ELxtbPACCCTElFTEVBQmGA" />

优点

  • 减少http请求次数
  • 做为背景平铺类的图片使用内联图片的话,减少http请求次数,并且不会影响加载速度

使用内联图片的缺点

  1. 浏览器不会缓存内联图片资源
  2. 兼容性较差,只支持ie8以上浏览器
  3. 超过1000kb的图片,base64编码会使图片大小增大,导致网页整体下载速度减慢

合并脚本和样式表

  • 当今最流行的单页应用的构建过程
    • 打包js 、css
    • 压缩js 、css

规则2——使用内容发布网络 CDN

CDN 一组分布在不同地理位置的web服务器,你访问这些资源的时候会选择离你位置最近的服务器把资源返回。

  • 用于发布静态资源如 图片、js、css等

规则3——使用 Expires头

  • Expires头就是给你一个过期时间 年月日时分秒,
    • 你请求一个图片资源时,回去对比这个时间 如果没到就不发请求

Expires头缺点:

  • 设置的过期时间是 2024年,但你系统时间是2050年~ 永远都会去发请求

Max-Age 和 mod_expires

  • HTTP1.1 引入了 Cache-Control头 ,解决了 Expires头的问题
  • 通过 Cache-Control: max-age=31536000 指定过期秒数

如何更新缓存

假设你的一个图片<img src="/xx.png"/> 如果你想更新它,但是它设置的 Expires 还没过期,此时即使你服务器端把文件替换了,但是用户看到的还是老的图片

  • 第一个方法 改文件名 <img src="/yy.png"/>
  • 第二个方法 时间戳 <img src="/xx.png?t=20200330152222"/>

规则3——压缩组件

HTTP实际就是本质就是“字节流”

所以传输过程中,文件的大小也会影响时间。200K的文件 压缩后可能是 100K ,这样在下载速度恒定的情况下,下载总耗时就少了

  • Accept-Encoding头
请求头 
Accept-Encoding: gzip, deflate, br

响应头
Content-Encoding: gzip

gzip

gzip压缩通常能压缩 66%的内容

代理缓存

  • 正常情况浏览器直接和服务器通信,没有任何问题,web服务器会根据 Accept-Encoding 来检测是否响应进行压缩。
  • 浏览器会基于 Cache-control 来缓存文件

过程

  • 001 当你的浏览器通过代理 访问 URL,而恰巧这个浏览器不支持 gzip ,此时代理就缓存一个 未经过gzip版本的文件。
  • 002 另一个浏览器通过代理访问服务器,这个浏览器支持 gzip,由于代理缓存的是 未压缩的版本,而代理会使用 未压缩版的缓存文件处理响应,这样得到的 就是未压缩的文件。

解决代理缓存的问题就是

  • 设置 Vary:Acceipt-Encoding 这将使代理缓存多个版本的文件。 压缩版和未压缩版

规则5——将样式表放在顶部

白屏问题

  • 就是刷新页面 半天白屏然后才显示页面

将CSS放在顶部

两种方式

<link rel="stylesheet" href="main.css">

<style>
@import url("main.css")
</style>
  • 推荐使用 link 代替 @import
  • 一个 style可以包含多个 @import 规则,但 @import 规则必须放在所有其他规则之前。
    • @import 可能会导致白屏,即便把他放到 head 里也是如此
    • 使用 @import 会导致组件下载时的无序性。

测试, 一个HTML页面 6个图片,一个 css

  • link 在 Head里 , 先 html,然后css ,最后图片
  • link 在 body里后, 先 html ,然后图片,然后css
  • @import 在 head里, 先html,然后图片,最后css

一个有意思的对比故事

相传 在老版本的IE是女汉子,chrome是小姐姐

  • 女汉子:出门在化妆
    • 在 IE你会先看到 html结构,然后样式逐渐呈现
  • 小姐姐:不化妆不出门
    • 在 chrome里, 如果css不加载完,我就不显示

规则6——将脚本放在底部

上一规则是把 样式表前置到 Head里让页面先呈现,而这次是把 js放在底部。

这样页面既可以逐步呈现,也可以提高下载的并行度

并行下载

对响应时间影响最大的是页面中组件的数量。当缓存为空时,每个组件都会产生一个HTTP请求,有时即使缓存是完整的也是如此。

浏览器会并行的执行HTTP请求,为什么HTTP请求的数量会影响响应时间呢?难道不能一次下载所有吗?

  • HTTP1.1 规范,该规范建议浏览器从每个主机名并行的下载两个组件(https://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.1.4)
    • 这样请求一个网站加载各种组件的时候,应该呈现 2个请求一组的 阶梯状下载的
  • 如果一个 web页面平均的将他的组件分别放在 两个主机名下,整体响应时间可以减少大约一半
  • 现在很多网站使用 HTTP1.1 ,但是他们的并行下载数量 大部分已经不是2个了。

增加并行下载数量比如8个,并不是没有开销的,其优劣取决于你的带宽和CPU速度,过多的并行下载反而会降低性能

脚本阻塞下载

  • 脚本可能使用 document.write 来修改页面内容,因此浏览器会等待,以确保页面能恰当的布局
  • 在浏览器下载脚本时,浏览器阻塞并行下载的另一个原因是:
    • 为了保证脚本能按照正确的顺序执行
    • 如果并行下载多个脚本,就无法保证响应是按照特定顺序达到浏览器的。
    <!DOCTYPE html>
    <html lang="en">
    <body>
        <img src="1.png" alt="">
        <img src="2.png" alt="">
        <script src="main.js"></script>
        <img src="3.png" alt="">
        <img src="4.png" alt="">
    </body>
    </html>
    
    如果 main.js 很大, 会阻塞加载 3.png 和 4.png
    
    • 这样是为了保证了 如果 多个脚本存在依赖关系的情况下保证 js的执行顺序

最差情况:将 js放在顶部

  • 脚本会阻塞对后面内容的呈现
  • 脚本会阻塞对其后面组件的下载

最佳情况:将js放在底部

  • 这样不会阻塞页面内容的呈现
<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="stylesheet" href="main.css">
</head>
<body>
    <img src="1.png" alt="">
    <img src="2.png" alt="">
    <img src="3.png" alt="">
    <img src="4.png" alt="">
    <script src="main.js"></script>
</body>
</html>

参考一些网站的页面html结构即可

规则7——避免使用CSS表达式

CSS表达式(css expression)可以动态设置CSS属性的强大(并危险)的方式

left: expression(document.body.offsetWidth - 180 "px");
top: expression(document.body.offsetHeight - -80 "px");

background:expression( (new Date()).getHours()%2 ?red:green);
  • 更新表达式会造成频繁计算
    width:expression(setCntr(),document.body.clientWidth<600 ? '600px':'auto');
    

CSS exprssion技术达到了可以使用表达式或公式来定义CSS属性的目的,MSDN针对CSS表达式给出的优点是:减少页面上的代码,使设计师无需学习JavaScript就能实现一些DHTML的效果。但这种减少代码主要是减少了JavaScript的代码。

CSS表达式缺陷:

  • 不符合WEB标准CSS表达式这种在表现中插入行为的JavaScript代码,有悖于Web标准的结构、表现、行为相分离的理念。
  • 效率低一个CSS表达式会反复执行,因为需要不停的去计算CSS的属性值,甚至执行成百上千次,这会大大消耗计算机硬件资源,极端情况下可能会导致浏览器崩溃。
  • 安全隐患CSS表达式暴露了一个脚本执行的上下文,可能带来脚本注入的隐患。

结论:无论何时都别用CSS表达式

规则8——使用外部JS和CSS

内联VS外置

纯粹而言,内联快一点

  • 内联:一个html ,js css都写在 html里
    • 它实际就触发一次请求
  • 外置:一个html 引入 js css
    • 至少三个http请求,虽然资源大小可能一样,而且每个请求都有耗时
    • 优点是: js和css可以被缓存,其实也就是第一次慢~

页面浏览量

  • 如果一段js/css被多页页面共用,当用户访问 1.html 时候会被缓存,在访问 2.html时就不会重新下载了
  • 使用外部 js、css的收益随着用户每月页面的浏览量而增加

空缓存 VS 完整缓存

  • 用户第一次请求 一个页面 由于 空缓存,每个资源都要下载一次
  • 用户第二次请求这个页面 由于部分 js \ css 已被缓存,则页面响应速度变快

组件重用

  • 多个页面使用相同的 js \ css 使用外部引入可以提高组件的重用率
    • 因为第二次访问的时候已经被缓存了

主页响应速度问题

有些主页为了保证响应速度内联 js和css。

但是他们还有其他页面 如 about.html / help.html

  • 其他页面使用了 外置的 js / css 该如何解决呢?
    • 首页加载后下载 在 home onload事件里 动态加载 js / css
    • 如果想避免对 home造成影响可以 使用 iframe 指定 外置 js/css的 url

动态内联

通过cookie,如果你是第一次访问本网站,肯定不 cookie,此时执行 下载外置 js \ css的逻辑

规则9——减少DNS查找

Internet 是通过 IP地址查找服务器的。 由于IP 很难记忆,于是采用 包含主机名的 URL代替。 当浏览器发送请求时,IP地址仍然需要,这就是 DNS 所处的角色。

  • DNS将主机名映射到 IP 上。 这样你访问 taobao.com 的时候就会去查找 淘宝对应的 IP.

DNS也是开销。

浏览器查找一个给定主机名的IP 地址需要20~120毫秒。在DNS找到之前,浏览器不能从主机下载到任何东西。

DNS缓存和TTL

当你访问 淘宝 发生了什么

  • 你请求一个主机后,DNS信息会缓存在操作系统的DNS缓存里。之后再次访问就无须DNS查找
  • 很多浏览器拥有自己的缓存,和操作系统缓存相分离,只要浏览器保留了 DNS缓存信息,就不会麻烦操作系统记录这个记录。

影响DNS缓存的因素

  • 服务器可以表明记录可以缓存多久,查找返回的DNS记录 包含了一个存活事件TTL值,该值告诉客户可以对该记录缓存多久

Keep-Alive

  • 默认情况下,一个持久的TCP连接会一直使用,直到其空闲1分钟为止。
    • 由于连接是持久的,因此无须 DNS查找
    • 另一个优点是:Keep-Alive 通过现有连接避免了重复的DNS查找

减少DNS查找

  • 当客户端 DNS缓存为空,DNS查找数量与 Web页面里唯一主机名的数量相等,包含 页面url,图片,js,css,等
    • 减少唯一主机名的数量可以减少 DNS查找数量
  • 尽可能使用持久连接,以消除TCP握手和慢启动延迟。

每一次主机名解析都需要一次网络往返,从而增加请求的延迟时间,同时还会阻塞后续的请求。

规则10——精简JS

  • 去除注释
  • 混淆
  • 压缩代码

现代前端各种 打包工具,基本实现了这个功能

规则11——避免重定向

  • 301
  • 302

重定向会让你的页面变慢

重定向类型

  • 300 Multiple Choices
  • 301 Moved Permancently
  • 302 Moved Temporarily
  • 303 See Other
  • 304 Not Modified
  • 305 Use Proxy
  • 306 不再使用
  • 307 Temporary Redirect

301/302都不会被缓存

  • 除非设置 Expires 和 Cache-Control

HTTP重定向很费时间,特别是不同域名之间的重定向,更加费时;这里面既有额外的DNS查询、TCP握手,还有其他延迟。最好的重定向次数为零。

规则12——删除重复脚本

确保脚本只被包含一次

规则13——配置ETag

ETag 是一种缓存机制

  • MD5摘要算法,变动越小差异越大
  • 第一次文件响应的时候 返回一个 文件 md5值
  • 下次请求同一资源的时候发送这个 md5 如果不同则重新下载,相同则不下载

详情参考 https://sltrust.github.io/2018/02/10/N044_02_Cache_Control/

  • 用 Cache Control是直接不请求,从盘符里读取资源
  • 用 ETag 是直接不下载,但是仍然会发请求

规则14——使用Ajax可缓存

比如 ajax请求 一些内容是万年不变的,

  • 这个时候如果用 Expires 就能从浏览器的缓存读取

但我感觉这可能是上古时代的场景,已经不适用于高速发展的今天了

posted @ 2020-11-24 15:42  almost陈  阅读(105)  评论(0)    收藏  举报