We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
我们知道在浏览器渲染中,页面渲染有几个关键的时刻比如说First Paint、DOMContentLoaded、Onload以及可交互时间。
First Paint
DOMContentLoaded
Onload
可交互时间
打开我们亲爱的淘宝页面,使用devtools中的Performance面板录制一段从初始加载到完成的过程,可以看出各个资源的下载和执行的过程,也能看到Chrome给我们标出了所需要注意的几个关键时间点。
devtools
Performance
Chrome
直接看字面意思,就是DOM的内容加载(解析)完毕了。而据我们之前所知,页面中脚本(无论是外链还是内联)的执行都会阻碍DOM的解析,也就是说脚本的执行,会延迟DOMContentLoaded事件的到来。
如上图所示,DOM的解析阻塞于脚本的加载,而脚本的加载也受限于脚本前面的css加载完成后才会执行,在任何情况下,DOMContentLoaded的触发不需要等待图片或者其他任何资源的加载完成。
这里插一个题外话,async标明的脚本不知道何时会加载完,而后立即执行,所以DOMContentLoaded事件也不会等它。但type=module和defer标明的<script>标签脚本一定会先于DOMContentLoaded事件。
async
type=module
defer
<script>
以下代码都是我们熟悉的用于监听DCL事件
DCL
// jQuery $(document).ready(function(){......}); // 或者 $(function(){...}); // 原生 document.addEventListener('DOMContentLoaded',function(){......})
首先回答是不可以的。 因为DCL的定义是整个文档都加载完成,当然也包括body外,HTML内的script标签。 但是我们要是把script标签放到了header中,往细说是阻塞了body的解析,那么body中有啥?当然就是我们页面的主要内容结构啦。
script
header
body
理论上浏览器会等待DOM和CSSOM都解析完生成RenderTree才开始布局和绘制,但是现代的浏览器,为了减少白屏等待的时间,都会进行HTML局部的渲染。
上面的截图同样来自于淘宝首页,我们可以看到在DOMContentLoaded之前,就已经触发了FirstPaint,页面空白的时间的不到30ms。但是DOM远远没有解析完,只是部分完成了。这个过程中,我们发现并没有表示script执行的黄色片段。
FirstPaint
30ms
下面我们来看看www.hoopchina.com 虎扑网的首页
www.hoopchina.com
�所以我们将script标签写在header中则会阻塞body的解析,也就是会阻碍First Paint的到来,也就是说用户看到的白屏时间会更长。
所以呢,js代码沉底,只是提前 First Content Paint 的时间,而不是减少 DOMContentLoaded 的时间。
First Content Paint
看到图中红色的块块了吗?...在ie 6-8下,请做以下兼容
document.onreadystatechange=function(){ /*dom加载完成的时候*/ if(document.readyState=='complete'){ fn&&fn();//处理事情 } };
�既然上面提到了FirstPaint那么我么就先说说First�Paint相关的知识。
首先呢,Chrome 的devtools给我们细微的划分了FirstPaint为First ���Contentful Paint(首次有内容的渲染)和First Meaningful Paint(首次有效的渲染)。
First ���Contentful Paint
First Meaningful Paint
使用 window.performace.getEntriesByType 这个api可以检测到这两个阶段的开始时间。
window.performace.getEntriesByType
FirstPaint 表示的是页面上第一个像素被绘制上去的时刻,有可能是背景颜色。
像素
FirstContentfulPaint 表示的是浏览器第一个DOM节点渲染到品目上的时间。
从上面的测试结果也可以看出来,二者之间的间距非常非常之小,但这两者共同决定了我们常说的白屏时间。
FMP在chrome下的定义是,浏览器计算页面的内容高度或者说是内容多少变化最大的一个时刻,和我们通常意义上将的首屏内容(不包括滚动下滑的内容)意义相近。内容有没有意义,也只是我们网站开发者才能够知道的,所以我们能够根据这一条规则进行优化。
首屏内容
还是来看看淘宝首页的情况吧,FCP的时候出现了顶部的搜索框,FMP的时候基本完成了骨架屏的渲染。
我们知道浏览器中的Javascript是单线程的,浏览器的渲染机制也规定了,UI渲染、�JS执行和用户操作一个时刻只能够执行一种,一定会有一个先后顺序。(宏任务微任务的概念看这里,传送门👉)
既然用户的操作会被JS的执行和UI渲染所阻塞,既然也不能完全避免这样的情况,我们就需要将这样的占用时间尽量缩短,或者说是切割。也就是说将长的JS代码执行任务,切割成小的执行任务。
切割
从人的感受上来说,用户给出的操作最好在100ms内要得到操作反馈,否则就会让用户感觉到卡顿或者说不爽。
100ms
卡顿
不爽
其实就是“可交互时间”的英文翻译,我们常简称为TTI。定义上来说,指的是用户看到了页面的大部分内容(近似于FMP)之后,准备进行用户操作,但是此时的主线程又被JS的执行所占用着,想输入,想点击但是都得不到浏览器的响应。
TTI
FMP
借用一张图来表示,图不是我自己画的....来源在这
在文章首部的图中,我们发现DOMContentLoaded之后还有一个Onload Event,它表明的是页面上所有的资源(图片、音频、视屏)都被加载完的时刻,就会触发onload事件,并且它是固定会晚于DCL时刻的。因为onload是指DOM中的所有资源,而影响DCL只有CSS和JS这两种资源的加载与执行。
Onload Event
重要的是,也能确保使用async标记的script被加载并且执行完,假如我们的一些业务代码是依赖于这些异步加载的第三方库的,则不会出现业务代码操作失败。
但因为图片、视屏这一类的资源一般都是加载时间较长,所以onload事件的使用,需要谨慎,否则会大大拖延业务代码的执行。
图片、视屏
onload
对应到代码上,就是使用JS去监听window.onload事件
window.onload
window.onload=function(){ document.getElementById("bg").style.backgroundColor="#F90"; //DOM操作 // 或者其他任意业务代码 }
总结一下,其实前端性能优化,就是服务于用户的感受的,说白了就是用户要感觉到爽,那么我们能不能够从交互的角度来量化一下用户的感受呢?
爽
以下是几个当你打开一个页面后,脑子里会�闪过的几个念头...
回看Timeline,阻塞FP和FCP的就是head标签中的css和Javascript,但是有些css确实首屏需要的(key-css的概念,看这里👉)则仍需要保留在head中避免频繁重排,Javascript脚本则可以放心地沉到body底部或者defer、async。
使用http缓存本地资源,减少请求时间也是很重要的一点。
FMP是关键内容的出现,或者是大部分内容出现的时间。在这一点上客户端的渲染能力,远远比不上服务端的渲染能力,所以首推SSR。
SSR
TTI强调的点是交互,那么我们要消除或者减少的是页面UI渲染和JS脚本执行。
UI渲染
JS脚本执行
TTL最后的这一条优化,可以根据网站功能类型的不同去做不同优化,不要墨守成规。
在非首屏情况下,大体不会有渲染的问题,但是用户的行为会触发javascript的执行,比如说复杂的JS计算也是会导致用户操作的卡顿,页面进入假死状态。
web-worker
[1] 前端性能量化标准 -by 云栖社区 [2] DOMContentLoaded与load的区别
The text was updated successfully, but these errors were encountered:
No branches or pull requests
我们知道在浏览器渲染中,页面渲染有几个关键的时刻比如说
First Paint
、DOMContentLoaded
、Onload
以及可交互时间
。打开我们亲爱的淘宝页面,使用
devtools
中的Performance
面板录制一段从初始加载到完成的过程,可以看出各个资源的下载和执行的过程,也能看到Chrome
给我们标出了所需要注意的几个关键时间点。DOMContentLoaded
直接看字面意思,就是DOM的内容加载(解析)完毕了。而据我们之前所知,页面中脚本(无论是外链还是内联)的执行都会阻碍DOM的解析,也就是说脚本的执行,会延迟
DOMContentLoaded
事件的到来。如上图所示,DOM的解析阻塞于脚本的加载,而脚本的加载也受限于脚本前面的css加载完成后才会执行,在任何情况下,DOMContentLoaded的触发不需要等待图片或者其他任何资源的加载完成。
这里插一个题外话,
async
标明的脚本不知道何时会加载完,而后立即执行,所以DOMContentLoaded
事件也不会等它。但type=module
和defer
标明的<script>
标签脚本一定会先于DOMContentLoaded
事件。以下代码都是我们熟悉的用于监听
DCL
事件Q:我们把script沉到body后面可以让DOMContentLoaded提前吗?
首先回答是不可以的。
因为
DCL
的定义是整个文档都加载完成,当然也包括body外,HTML内的script标签。但是我们要是把
script
标签放到了header
中,往细说是阻塞了body
的解析,那么body中有啥?当然就是我们页面的主要内容结构啦。理论上浏览器会等待DOM和CSSOM都解析完生成RenderTree才开始布局和绘制,但是现代的浏览器,为了减少白屏等待的时间,都会进行HTML局部的渲染。
上面的截图同样来自于淘宝首页,我们可以看到在DOMContentLoaded之前,就已经触发了
FirstPaint
,页面空白的时间的不到30ms
。但是DOM远远没有解析完,只是部分完成了。这个过程中,我们发现并没有表示script
执行的黄色片段。下面我们来看看
www.hoopchina.com
虎扑网的首页�所以我们将script标签写在header中则会阻塞body的解析,也就是会阻碍
First Paint
的到来,也就是说用户看到的白屏时间会更长。所以呢,js代码沉底,只是提前
First Content Paint
的时间,而不是减少DOMContentLoaded
的时间。兼容性(ie滚粗)
看到图中红色的块块了吗?...在ie 6-8下,请做以下兼容
�既然上面提到了FirstPaint那么我么就先说说First�Paint相关的知识。
FirstPaint
首先呢,Chrome 的devtools给我们细微的划分了FirstPaint为
First ���Contentful Paint
(首次有内容的渲染)和First Meaningful Paint
(首次有效的渲染)。FirstPaint && FirstContentfulPaint
使用
window.performace.getEntriesByType
这个api可以检测到这两个阶段的开始时间。FirstPaint 表示的是页面上第一个
像素
被绘制上去的时刻,有可能是背景颜色。FirstContentfulPaint 表示的是浏览器第一个DOM节点渲染到品目上的时间。
从上面的测试结果也可以看出来,二者之间的间距非常非常之小,但这两者共同决定了我们常说的白屏时间。
FisrtMeaningfulPaint
FMP在chrome下的定义是,浏览器计算页面的内容高度或者说是内容多少变化最大的一个时刻,和我们通常意义上将的
首屏内容
(不包括滚动下滑的内容)意义相近。内容有没有意义,也只是我们网站开发者才能够知道的,所以我们能够根据这一条规则进行优化。还是来看看淘宝首页的情况吧,FCP的时候出现了顶部的搜索框,FMP的时候基本完成了骨架屏的渲染。
可交互时间
我们知道浏览器中的Javascript是单线程的,浏览器的渲染机制也规定了,UI渲染、�JS执行和用户操作一个时刻只能够执行一种,一定会有一个先后顺序。(宏任务微任务的概念看这里,传送门👉)
既然用户的操作会被JS的执行和UI渲染所阻塞,既然也不能完全避免这样的情况,我们就需要将这样的占用时间尽量缩短,或者说是
切割
。也就是说将长的JS代码执行任务,切割成小的执行任务。从人的感受上来说,用户给出的操作最好在
100ms
内要得到操作反馈,否则就会让用户感觉到卡顿
或者说不爽
。Time to interactive
其实就是“可交互时间”的英文翻译,我们常简称为
TTI
。定义上来说,指的是用户看到了页面的大部分内容(近似于FMP
)之后,准备进行用户操作,但是此时的主线程又被JS的执行所占用着,想输入,想点击但是都得不到浏览器的响应。借用一张图来表示,图不是我自己画的....来源在这
Onload Evnet
在文章首部的图中,我们发现
DOMContentLoaded
之后还有一个Onload Event
,它表明的是页面上所有的资源(图片、音频、视屏)都被加载完的时刻,就会触发onload事件,并且它是固定会晚于DCL
时刻的。因为onload是指DOM中的所有资源,而影响DCL
只有CSS和JS这两种资源的加载与执行。重要的是,也能确保使用
async
标记的script
被加载并且执行完,假如我们的一些业务代码是依赖于这些异步加载的第三方库的,则不会出现业务代码操作失败。但因为
图片、视屏
这一类的资源一般都是加载时间较长,所以onload
事件的使用,需要谨慎,否则会大大拖延业务代码的执行。对应到代码上,就是使用JS去监听
window.onload
事件用户的感受
总结一下,其实前端性能优化,就是服务于用户的感受的,说白了就是用户要感觉到
爽
,那么我们能不能够从交互的角度来量化一下用户的感受呢?以下是几个当你打开一个页面后,脑子里会�闪过的几个念头...
如何优化
1️⃣ 如何提前FP和FCP
回看Timeline,阻塞FP和FCP的就是head标签中的css和Javascript,但是有些css确实首屏需要的(key-css的概念,看这里👉)则仍需要保留在head中避免频繁重排,Javascript脚本则可以放心地沉到body底部或者
defer
、async
。使用http缓存本地资源,减少请求时间也是很重要的一点。
2️⃣ 如何提前FMP
FMP是关键内容的出现,或者是大部分内容出现的时间。在这一点上客户端的渲染能力,远远比不上服务端的渲染能力,所以首推
SSR
。3️⃣ 如何提前TTI
TTI强调的点是交互,那么我们要消除或者减少的是页面
UI渲染
和JS脚本执行
。DCL
之前去加载执行,而不是让用户看到了页面再去等待加载时间,因为用户看到白屏至少不会想要去操作,或者说用户看到了所有内容就会立刻想去操作,若得不到反馈,会极为的不爽。4️⃣ 如何减少TTI后,对用户操作的干扰
在非首屏情况下,大体不会有渲染的问题,但是用户的行为会触发javascript的执行,比如说复杂的JS计算也是会导致用户操作的卡顿,页面进入假死状态。
web-worker
进行多线程计算,然后返回结果到主线程。参考文章
[1] 前端性能量化标准 -by 云栖社区
[2] DOMContentLoaded与load的区别
The text was updated successfully, but these errors were encountered: