参考资料:前端总结--性能优化
参考资料:js和css的加载形成阻塞化
参考资料:浏览器的工做原理
参考资料:css 阻塞 js阻塞
参考资料:分布渲染
参考资料:css加载会形成阻塞吗
参考资料:干货 | 十分钟读懂浏览器渲染流程javascript
首先咱们须要经过 DNS(域名解析系统)将 URL 解析为对应的 IP 地址,而后与这个 IP 地址肯定的那台服务器创建起 TCP 网络链接,随后咱们向服务端抛出咱们的 HTTP 请求,服务端处理完咱们的请求以后,把目标数据放在 HTTP 响应里返回给客户端,拿到响应数据的浏览器就能够开始走一个渲染的流程。渲染完毕,页面便呈现给了用户 css
输入 URL 到显示页面这个过程当中,涉及到网络层面的,有三个主要过程:html
通常来讲,在前端优化中与 DNS 有关的有两点:前端
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">
复制代码
对于 DNS 解析和 TCP 链接两个步骤,咱们前端能够作的努力很是有限。相比之下,HTTP 链接这一层面的优化才是咱们网络优化的核心git
HTTP 优化有两个大的方向
雪碧图
多张小图片合并为一张图,利用CSS -background-position调整图片显示位置
图标字体文件
阿里图标
前置知识:二进制位数与色彩的关系 在计算机中,像素用二进制数来表示。不一样的图片格式中像素与二进制位数之间的对应关系是不一样的。一个像素对应的二进制位数越多,它能够表示的颜色种类就越多,成像效果也就越细腻,文件体积相应也会越大。
关键字:有损压缩、体积小、加载快、不支持透明
JPG 的优势
JPG 最大的特色是有损压缩。这种高效的压缩算法使它成为了一种很是轻巧的图片格式。另外一方面,即便被称为“有损”压缩,JPG的压缩方式仍然是一种高质量的压缩方式:当咱们把图片体积压缩至原有体积的 50% 如下时,JPG 仍然能够保持住 60% 的品质。此外,JPG 格式以 24 位存储单个图,能够呈现多达 1600 万种颜色,足以应对大多数场景下对色彩的要求,这一点决定了它压缩先后的质量损耗并不容易被咱们人类的肉眼所察觉——前提是你用对了业务场景。
使用场景
JPG 适用于呈现色彩丰富的图片,在咱们平常开发中,JPG 图片常常做为大的背景图、轮播图或 Banner 图出现。
两大电商网站对大图的处理,是 JPG 图片应用场景的最佳写照: 打开淘宝首页,咱们能够发现页面中最醒目、最庞大的图片,必定是以 .jpg 为后缀的: 使用 JPG 呈现大图,既能够保住图片的质量,又不会带来使人头疼的图片体积,是当下比较推崇的一种方案。
JPG 的缺陷
有损压缩在上文所展现的轮播图上确实很难露出马脚,但当它处理矢量图形和 Logo 等线条感较强、颜色对比强烈的图像时,人为压缩致使的图片模糊会至关明显。此外,JPEG 图像不支持透明度处理,透明图片须要召唤 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 格式:
关键字:文本文件、体积小、不失真、兼容性好
SVG 的使用方式与应用场景
将 SVG 写入 HTML
将 SVG 写入独立文件后引入 HTML将 SVG 写入 HTML
关键字:文本文件、依赖编码、小图标解决方案
Base64 的应用场景
图片的实际尺寸很小(你们能够观察一下掘金页面的 Base64 图,几乎没有超过 2kb 的)
图片没法以雪碧图的形式与其它小图结合(合成雪碧图还是主要的减小 HTTP 请求的途径,Base64 是雪碧图的补充)
图片的更新频率很是低(不需咱们重复编码和修改文件内容,维护成本较低)
关键字:年轻的全能型选手 是 Google 专为 Web 开发的一种旨在加快图片加载速度的图片格式,它支持有损压缩和无损压缩。
WebP 的优势
WebP 像 JPEG 同样对细节丰富的图片信手拈来,像 PNG 同样支持透明,像 GIF 同样能够显示动态图片——它集多种图片文件格式的优势于一身。
WebP 的局限性
兼容性
不一样业务场景下的图片方案选型
对于一个数据请求来讲,能够分为发起网络请求、后端处理、浏览器响应三个步骤
浏览器缓存能够帮助咱们在第一和第三步骤中优化性能。好比说直接使用缓存而不发起请求,或者发起了请求但后端存储的数据和前端一致,那么就没有必要再将数据回传回来,这样就减小了响应数据。
缓存思惟导图
从缓存位置上来讲分为四种,而且各自有优先级,当依次查找缓存且都没有命中的时候,才会去请求网络。
Service Worker
Memory Cache
Disk Cache
Push Cache
不了解 MDN
MemoryCache,是指存在内存中的缓存。从优先级上来讲,它是浏览器最早尝试去命中的一种缓存。从效率上来讲,它是响应速度最快的一种缓存。
内存缓存是快的,也是“短命”的。它和渲染进程“生死相依”,当进程结束后,也就是 tab 关闭之后,内存里的数据也将不复存在。
那么哪些文件会被放入内存呢?
事实上,这个划分规则,一直以来是没有定论的。不过想一想也能够理解,内存是有限的,不少时候须要先考虑即时呈现的内存余量,再根据具体的状况决定分配给内存和磁盘的资源量的比重——资源存放的位置具备必定的随机性
虽然划分规则没有定论,但根据平常开发中观察的结果,包括咱们开篇给你们展现的 Network 截图,咱们至少能够总结出这样的规律:资源存不存内存,浏览器秉承的是“节约原则”。咱们发现,Base64 格式的图片,几乎永远能够被塞进 memory cache,这能够视做浏览器为节省渲染开销的“自保行为”;此外,体积不大的 JS、CSS 文件,也有较大地被写入内存的概率——相比之下,较大的 JS、CSS 文件就没有这个待遇了,内存资源是有限的,它们每每被直接甩进磁盘。
Disk Cache 也就是存储在硬盘中的缓存,读取速度慢点,可是什么都能存储到磁盘中,比之 Memory Cache 胜在容量和存储时效性上。
在全部浏览器缓存中,Disk Cache 覆盖面基本是最大的。它会根据 HTTP Herder 中的字段判断哪些资源须要缓存,哪些资源能够不请求直接使用,哪些资源已通过期须要从新请求。而且即便在跨站点的状况下,相同地址的资源一旦被硬盘缓存下来,就不会再次去请求数据。绝大部分的缓存都来自 Disk Cache,关于 HTTP 的协议头中的缓存字段,咱们会在下文进行详细介绍
浏览器会把哪些文件丢进内存中?哪些丢进硬盘中 对于大文件来讲,大几率是不存储在内存中的,反之优先 当前系统内存使用率高的话,文件优先存储进硬盘
不了解
push Cache(推送缓存)是 HTTP/2 中的内容,当以上三种缓存都没有命中时,它才会被使用
浏览器与服务器通讯的方式为应答模式,便是:浏览器发起HTTP请求 – 服务器响应该请求,那么浏览器怎么肯定一个资源该不应缓存,如何去缓存呢?浏览器第一次向服务器发起该请求后拿到请求结果后,将请求结果和缓存标识存入浏览器缓存,浏览器对于缓存的处理是根据第一次请求资源时返回的响应头来肯定的。具体过程以下图:
HTTP 缓存是咱们平常开发中最为熟悉的一种缓存机制。它又分为强缓存和协商缓存。优先级较高的是强缓存,在命中强缓存失败的状况下,才会走协商缓存。
强缓存:不会向服务器发送请求,直接从缓存中读取资源,在chrome控制台的Network选项中能够看到该请求返回200的状态码,而且Size显示from disk cache或from memory cache。强缓存能够经过设置两种 HTTP Header 实现:Expires 和 Cache-Control。
缓存过时时间,用来指定资源到期的时间,是服务器端的具体的时间点。也就是说,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 后过时,须要再次请求。
在HTTP/1.1中,Cache-Control是最重要的规则,主要用于控制网页缓存Cache-Control 能够在请求头或者响应头中设置,而且能够组合使用多种指令
max-age
max-age=xxx (xxx is numeric)表示缓存内容将在xxx秒后失效
s-maxage
private
全部内容只有客户端能够缓存
表示中间节点不容许缓存,对于Browser <-- proxy1 <-- proxy2 <-- Server,proxy 会老老实实把Server 返回的数据发送给proxy1,本身不缓存任何数据。当下次Browser再次请求时proxy会作好请求转发而不是自做主张给本身缓存的数据
public
全部内容都将被缓存(客户端和代理服务器均可缓存)
具体来讲响应可被任何中间节点缓存,如 Browser <-- proxy1 <-- proxy2 <-- Server,中间的proxy能够缓存资源,好比下次再请求同一资源proxy1直接把本身缓存的东西给 Browser 而再也不向proxy2要。
no-store
全部内容都不会被缓存,即不使用强制缓存,也不使用协商缓存
no-cache
客户端缓存内容,是否使用缓存则须要通过协商缓存来验证决定。表示不使用 Cache-Control的缓存控制方式作前置验证,而是使用 Etag 或者Last-Modified字段来控制缓存。须要注意的是,no-cache这个名字有一点误导。设置了no-cache以后,并非说浏览器就再也不缓存数据,只是浏览器在使用缓存数据时,须要先确认一下数据是否还跟服务器保持一致
4.6.1 什么是协商缓存
协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,主要有如下两种状况:
协商缓存生效,返回304和Not Modified
协商缓存失效,返回200和请求结果
Last-Modified 是一个响应首部,其中包含源头服务器认定的资源作出修改的日期及时间。 它一般被用做一个验证器来判断接收到的或者存储的资源是否彼此一致。因为精确度比 ETag 要低,因此这是一个备用机制。包含有 If-Modified-Since 或 If-Unmodified-Since 首部的条件请求会使用这个字段。
ETagHTTP响应头是资源的特定版本的标识符。这可让缓存更高效,并节省带宽,由于若是内容没有改变,Web服务器不须要发送完整的响应。而若是内容发生了变化,使用ETag有助于防止资源的同时更新相互覆盖(“空中碰撞”)
浏览器缓存机制介绍与缓存策略剖析
一文读懂前端缓存
深刻理解浏览器的缓存机制
存储容量大: Web Storage 根据浏览器的不一样,存储容量能够达到 5-10M 之间 仅位于浏览器端,不与服务端发生通讯。
生命周期: Local Storage 是持久化的本地存储,存储在其中的数据是永远不会过时的,使其消失的惟一办法是手动删除;而 Session Storage 是临时性的本地存储,它是会话级别的存储,当会话结束(页面被关闭)时,存储内容也随之被释放。
做用域: Local Storage、Session Storage 和 Cookie 都遵循同源策略。但 Session Storage 特别的一点在于,即使是相同域名下的两个页面,只要它们不在同一个浏览器窗口中打开,那么它们的 Session Storage 内容便没法共享。
Local Storage
Session Storage
微博的 Session Storage 就主要是存储你本次会话的浏览足迹
CDN (Content Delivery Network,即内容分发网络)指的是一组分布在各个地区的服务器。这些服务器存储着数据的副本,所以服务器能够根据哪些服务器与用户距离最近,来知足数据的请求。 CDN 提供快速服务,较少受高流量影响。
缓存、本地存储带来的性能提高,是否是只能在“获取到资源并把它们存起来”这件事情发生以后?也就是说,首次请求资源的时候,这些招数都是救不了咱们的。要提高首次请求的响应能力,咱们还须要借助 CDN 的能力
假设个人根服务器在杭州,同时在图示的五个城市里都有本身可用的机房
此时有一位北京的用户向我请求资源。在网络带宽小、用户访问量大的状况下,杭州的这一台服务器或许不那么给力,不能给用户很是快的响应速度。因而我灵机一动,把这批资源 copy 了一批放在北京的机房里。当用户请求资源时,就近请求北京的服务器,北京这台服务器低头一看,这个资源我存了,离得这么近,响应速度确定噌噌的!那若是北京这台服务器没有 copy 这批资源呢?它会再向杭州的根服务器去要这个资源。在这个过程当中,北京这台服务器就扮演着 CDN 的角色。
CDN 的核心点有两个,一个是缓存,一个是回源。
缓存
“缓存”就是说咱们把资源 copy 一份到 CDN 服务器上这个过程
回源
就是说 CDN 发现本身没有这个资源(通常是缓存的数据过时了),转头向根服务器(或者它的上层服务器)去要这个资源的过程。
CDN 每每被用来存放静态资源
“根服务器”本质上是业务服务器,它的核心任务在于生成动态页面或返回非纯静态页面,这两种过程都是须要计算的。业务服务器仿佛一个车间,车间里运转的机器轰鸣着为咱们产出所需的资源;相比之下,CDN 服务器则像一个仓库,它只充当资源的“栖息地”和“搬运工”。
所谓“静态资源”,就是像 JS、CSS、图片等不须要业务服务器进行计算即得的资源。而“动态资源”,顾名思义是须要后端实时动态生成的资源,较为常见的就是 JSP、ASP 或者依赖服务端渲染获得的 HTML 页面。
什么是“非纯静态资源”呢?它是指须要服务器在页面以外做额外计算的 HTML 页面。具体来讲,当我打开某一网站以前,该网站须要经过权限认证等一系列手段确认个人身份、进而决定是否要把 HTML 页面呈现给我。这种状况下 HTML 确实是静态的,但它和业务服务器的操做耦合,咱们把它丢到CDN 上显然是不合适的。
静态资源自己具备访问频率高、承接流量大的特色,所以静态资源加载速度始终是前端性能的一个很是关键的指标。CDN 是静态资源提速的重要手段,在许多一线的互联网公司,“静态资源走 CDN”并非一个建议,而是一个规定
能够看到业务服务器确实是返回给了咱们一个还没有被静态资源加持过的简单 HTML 页面
随便点开一个静态资源,能够看到它都是从 CDN 服务器上请求来的
Cookie 是紧跟域名的。同一个域名下的全部请求,都会携带 Cookie。你们试想,若是咱们此刻仅仅是请求一张图片或者一个 CSS 文件,咱们也要携带一个 Cookie 跑来跑去(关键是 Cookie 里存储的信息我如今并不须要),这是一件多么劳民伤财的事情。Cookie 虽然小,请求却能够有不少,随着请求的叠加,这样的没必要要的 Cookie 带来的开销将是没法想象的
同一个域名下的请求会不分青红皂白地携带 Cookie,而静态资源每每并不须要 Cookie 携带什么认证信息。把静态资源和主页面置于不一样的域名下,完美地避免了没必要要的 Cookie 的出现!
看起来是一个不起眼的小细节,但带来的效用倒是惊人的。以电商网站静态资源的流量之庞大,若是没把这个多余的 Cookie 拿下来,不只用户体验会大打折扣,每一年因性能浪费带来的经济开销也将是一个很是恐怖的数字。
客户端渲染模式下,服务端会把渲染须要的静态文件发送给客户端,客户端加载过来以后,本身在浏览器里跑一遍 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 源文件里里找不到——这正是它的特色。
服务端渲染的模式下,当用户第一次请求页面时,由服务器把须要的组件或页面渲染成 HTML 字符串,而后把它返回给客户端。客户端拿到手的,是能够直接渲染而后呈现给用户的 HTML 内容,不须要为了生成 DOM 内容本身再去跑一遍 JS 代码。
使用服务端渲染的网站,能够说是“所见即所得”,页面上呈现的内容,咱们在 html 源文件里也能找到
事实上,不少网站是出于效益(seo)的考虑才启用服务端渲染,性能却是在其次。
假设 A 网站页面中有一个关键字叫“前端性能优化”,这个关键字是 JS 代码跑过一遍后添加到 HTML 页面中的。那么客户端渲染模式下,咱们在搜索引擎搜索这个关键字,是找不到 A 网站的——搜索引擎只会查找现成的内容,不会帮你跑 JS 代码。A 网站的运营方见此情形,感到很头大:搜索引擎搜不出来,用户找不到咱们,谁还会用个人网站呢?为了把“现成的内容”拿给搜索引擎看,A 网站不得不启用服务端渲染。
但性能在其次,不表明性能不重要。服务端渲染解决了一个很是关键的性能问题——首屏加载速度过慢。在客户端渲染模式下,咱们除了加载 HTML,还要等渲染所需的这部分 JS 加载完,以后还得把这部分 JS 在浏览器上再跑一遍。这一切都是发生在用户点击了咱们的连接以后的事情,在这个过程结束以前,用户始终见不到咱们网页的庐山真面目,也就是说用户一直在等!相比之下,服务端渲染模式下,服务器给到客户端的已是一个直接能够拿来呈现给用户的网页,中间环节早在服务端就帮咱们作掉了,用户岂不“美滋滋”?。
服务端渲染本质上是本该浏览器作的事情,分担给服务器去作。这样当资源抵达浏览器时,它呈现的速度就快了
但仔细想一想,在这个网民遍地的时代,几乎有多少个用户就有多少台浏览器。用户拥有的浏览器总量多到数不清,那么一个公司的服务器又有多少台呢?咱们把这么多台浏览器的渲染压力集中起来,分散给相比之下数量并很少的服务器,服务器确定是承受不住的
除非网页对性能要求过高了,以致于全部的招式都用完了,性能表现仍是不尽人意,这时候咱们就能够考虑向老板多申请几台服务器,把服务端渲染搞起来了~
浏览器内核能够分红两部分:渲染引擎(Layout Engine 或者 Rendering Engine)和 JS 引擎
渲染引擎又包括了 HTML 解释器、CSS 解释器、布局、网络、存储、图形、音视频、图片解码器等等零部件。
#myList li {}
复制代码
浏览器必须遍历页面上每一个 li 元素,而且每次都要去确认这个 li 元素的父元素 id 是否是 myList
HTML、CSS 和 JS,都具备阻塞渲染的特性。 HTML 阻塞,天经地义——没有 HTML,何来 DOM?没有 DOM,渲染和优化,都是空谈。
在刚刚的过程当中,咱们提到 DOM 和 CSSOM 协力才能构建渲染树。这一点会给性能形成严重影响:默认状况下,CSS 是阻塞的资源。浏览器在构建 CSSOM 的过程当中,不会渲染任何已处理的内容。即使 DOM 已经解析完毕了,只要 CSSOM 不 OK,那么渲染这个事情就不 OK(这主要是为了不没有 CSS 的 HTML 页面丑陋地“裸奔”在用户眼前)。
只有当咱们开始解析 HTML 后、解析到 link 标签或者 style 标签时,CSS 才登场,CSSOM 的构建才开始。 不少时候,DOM 不得不等待 CSSOM。所以咱们能够这样总结:
CSS 是阻塞渲染的资源。须要将它尽早、尽快地下载到客户端,以便缩短首次渲染的时间。
尽早(将 CSS 放在 head 标签里)和尽快(启用 CDN 实现静态资源加载速度的优化)
JS 的做用在于修改,它帮助咱们修改网页的方方面面:内容、样式以及它如何响应用户交互。这“方方面面”的修改,本质上都是对 DOM 和 CSSDOM 进行修改。所以 JS 的执行会阻止 CSSOM,在咱们不做显式声明的状况下,它也会阻塞 DOM。
JS 引擎是独立于渲染引擎存在的。咱们的 JS 代码在文档的何处插入,就在何处执行。当 HTML 解析器遇到一个 script 标签时,它会暂停渲染过程,将控制权交给 JS 引擎。JS 引擎对内联的 JS 代码会直接执行,对外部 JS 文件还要先获取到脚本、再进行执行。等 JS 引擎运行完毕,浏览器又会把控制权还给渲染引擎,继续 CSSOM 和 DOM 的构建。 所以与其说是 JS 把 CSS 和 HTML 阻塞了,不如说是 JS 引擎抢走了渲染引擎的控制权。
现代浏览器会并行加载js文件。
加载或者执行js时会阻塞对标签的解析,也就是阻塞了dom树的造成,只有等到js执行完毕,浏览器才会继续解析标签。没有dom树,浏览器就没法渲染,因此当加载很大的js文件时,能够看到页面很长时间是一片空白
之因此会阻塞对标签的解析是由于加载的js中可能会建立,删除节点等,这些操做会对dom树产生影响,若是不阻塞,等浏览器解析完标签生成dom树后,js修改了某些节点,那么浏览器又得从新解析,而后生成dom树,性能比较差
正常模式
这种状况下 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。
当咱们使用 Vue 或 React 提供的接口去更新数据时,这个更新并不会当即生效,而是会被推入到一个队列里。待到适当的时机,队列中的更新任务会被批量触发。这就是异步更新。
异步更新能够帮助咱们避免过分渲染,是咱们上节提到的“让 JS 为 DOM 分压”的典范之一。
异步更新的特性在于它只看结果,所以渲染引擎不须要为过程买单
有时咱们会遇到这样的状况
// 任务一
this.content = '第一次测试'
// 任务二
this.content = '第二次测试'
// 任务三
this.content = '第三次测试'
复制代码
咱们在三个更新任务中对同一个状态修改了三次,若是咱们采起传统的同步更新策略,那么就要操做三次 DOM。但本质上须要呈现给用户的目标内容其实只是第三次的结果,也就是说只有第三次的操做是有意义的——咱们白白浪费了两次计算。
但若是咱们把这三个任务塞进异步更新队列里,它们会先在 JS 的层面上被批量执行完毕。当流程走到渲染这一步时,它仅仅须要针对有意义的计算结果操做一次 DOM——这就是异步更新的妙处。
Vue在观察到数据变化时并非直接更新DOM,而是开启一个队列,并缓冲在同一事件循环中发生的全部数据改变。在缓冲时会去除重复数据,从而避免没必要要的计算和DOM操做。而后,在下一个事件循环tick中,Vue刷新队列并执行实际工做
$nextTick就是用来知道何时DOM更新完成的
根据渲染树布局,计算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。
这两个东西都以闭包的形式存在。
它们经过对事件对应的回调函数进行包裹、以自由变量的形式缓存时间信息,最后用 setTimeout 来控制事件的触发频率。
在某段时间内,无论你触发了多少次回调,我都只认第一次,并在计时结束时给予响应。
如今有一个旅客刚下了飞机,须要用车,因而打电话叫了该机场惟一的一辆机场大巴来接。司机开到机场,心想来都来了,多接几我的一块儿走吧,这样这趟才跑得值——我等个十分钟看看。因而司机一边打开了计时器,一边招呼后面的客人陆陆续续上车。在这十分钟内,后面下飞机的乘客都只能乘这一辆大巴,十分钟过去后,无论后面还有多少没挤上车的乘客,这班车都必须发走
在某段时间内,无论你触发了多少次回调,我都只认最后一次。
第一个乘客上车后,司机开始计时(好比说十分钟)。十分钟以内,若是又上来了一个乘客,司机会把计时器清零,从新开始等另外一个十分钟(延迟了等待)。直到有这么一位乘客,从他上车开始,后续十分钟都没有新乘客上车,司机会认为确实没有人须要搭这趟车了,才会把车开走。
防抖 的问题在于它“太有耐心了”。试想,若是用户的操做十分频繁——他每次都不等 防抖 设置的 delay 时间结束就进行下一次操做,因而每次 防抖 都为该用户从新生成定时器,回调函数被延迟了不可胜数次。频繁的延迟会致使用户迟迟得不到响应,用户一样会产生“这个页面卡死了”的观感。 为了不弄巧成拙,咱们须要借力 节流 的思想,打造一个“有底线”的 防抖——等你能够,但我有个人原则:delay 时间内,我能够为你从新生成定时器;但只要delay的时间到了,我必需要给用户一个响应。这个 节流与 防抖 “合体”思路,已经被不少成熟的前端库应用到了它们的增强版 节流 函数的实现中
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))
}
复制代码