html页面加载完成后会触发的事件_前端隐秘角落 - 页面渲染

356660e4351c202fdcd29370677f16c4.png

前言

11b38679c82dcda219204e440c1c863a.png

如图所示,webkit内核浏览器的渲染过程(解析HTML,构建DOM树,解析CSS,构建CSSOM树 ,构建render树,布局layout,绘制painting),这些过程理解起来可能有些抽象,今天我们一起通过chrome开发者工具来直观的理解一下浏览器渲染页面的过程。

页面渲染过程

Performance工具

我们将通过Performance工具来分析页面渲染过程,首先按Command+ Option+ I(Mac)或 Control+ Shift+ I(Windows,Linux)打开DevTools,然后打开performance工具的界面。

ae5afd206736eb08223b323ae7fb66e2.png

我们可以看到Performance 的默认引导页面:

  • 第一句提示语所对应的操作是立即开始记录当前页面发生的所有事件,点击停止按钮才会停止记录。
  • 第二句对应的操作则是重载页面并记录事件,工具会自动在页面加载完毕处于可交互状态时停止记录。

两者最终都会生成报告(生成报告需要大量运算,会花费一些时间)。我们以实际前端项目(vue项目)为例,点刷新按钮重载页面并记录事件,得到如下报告。

55e3461025e65176254c706bd647293b.png

网络请求

这里提到网络请求,是因为浏览器渲染页面离不开网络请求,浏览器渲染进程开始接收HTML数据的标志,其实是收到了浏览器的导航的确认信息,浏览器的导航过程主要是在网络层,涉及DNS解析,浏览器强缓存/协商缓存,TCP三次握手建立连接,http请求响应,TCP四次挥手断开连接等多个环节,这里可以借助performance工具来理解浏览器渲染页面之前的网络请求部分:

9b3dbe6d42d48c8f16759f8ef710fe2b.png

上图中,可看到Network模块中有不同的颜色请求,它们分别代表的是蓝色-HTML、黄色-JS、绿色-图片的资源请求,如果有CSS文件的请求的话,它会被标识为紫色。另外一个细节点是,图中每个资源请求的左上角都有一个小方块,深蓝色的小方块代表较高优先级的请求,浅蓝色则代表较低优先级,很明显优先请求的资源(html、webpack打包的manifest、vendor、app主文件请求)都标识了深蓝色的小方块。

365bf5b43d624c61232a851761290a43.png

我们再来看Event log中浏览器的活动:

  • 请求html
  • 接收响应头
  • 浏览器接收到文档,开始解析处理
  • html响应数据已被接收
  • 网络请求完成

另外,图中可看到在浏览器发送请求之前,还做了一系列的事情,这是因为我们上面选择重载页面记录事件,所以在send requerst请求Html之前,会触发浏览器一系列默认事件行为:webkitvisibilitychange,unloadEventStart,unloadEventEnd等。

Parse HTML

在浏览器渲染引擎内部,有一个叫HTML 解析器(HTMLParser)的模块,它负责将HTML字节流转换为DOM结构。HTML Standard规范定义了浏览器渲染HTML为DOM的方法。

  • HTML解析器并不是等整个文档加载完成之后再解析的,而是网络进程加载了多少数据,HTML解析器就解析多少数据。

浏览器收到响应之后,我们来看下浏览器拿到的html文本

<!DOCTYPE html>
<html><head><meta charset=utf-8><title></title><link rel=dns-prefetch href=//s1.zhuanstatic.com>...<meta name=description content=转转><meta name=viewport content="width=device-width,viewport-fit=cover,initial-scale=1,maximum-scale=1,user-scalable=no"><meta content="telephone=no,email=no" name=format-detection><meta name=apple-mobile-web-app-capable content=yes><meta name=apple-mobile-web-app-status-bar-style content=default>
</head><body><div id=app></div><script type=text/javascriptsrc=https://s1.zhuanstatic.com/u/bmmain/static/js/manifest.f741ad8a3e48f84ad413.js></script><script type=text/javascriptsrc=https://s1.zhuanstatic.com/u/bmmain/static/js/vendor.6858ed31f5c34c2f6f8e.js></script><script type=text/javascript src=https://s1.zhuanstatic.com/u/bmmain/static/js/app.9ff176b78d7af021c2b0.js></script>
</body></html>

再来观察Event Log中浏览器第一次解析HTML的活动:解析html文本,遇到<body>中的<script>标签,发送了三个请求,分别请求manifest.js,vendor.js,app.js文件

5880d6c4eaf3b2559e98bbfa14c2dd37.png

JS的下载与执行

33afcf0a978927831223b5266a78b1df.png

上图显示,解析HTML的过程中遇到 <script> 标签时,渲染进程会停止解析HTML,而去加载,解析和执行js代码,当脚本执行完成之后,HTML解析器才会恢复解析过程,而js外链资源的加载,解析和执行通常又会很耗时,这就是前端常提及到的js阻塞的原因。当然,浏览器设置停止解析HTML的机制也是有原因的,因为js可能会改变DOM的结构(例如使用document.write等API)。不过我们也有多种方式来告知浏览器应对如何应对某个资源,例如在<script> 标签上添加了 asyncdefer 等属性,浏览器会异步的加载和执行JS代码,而不会阻塞渲染。

加载次优先级的资源

html页面中有CSS,JS,字体等额外的资源,这些资源也需要从网络上或者浏览器缓存中获取。主进程可以在构建 DOM 的过程中会逐一请求它们,为了加速,浏览器的预加载扫描器会同时运行,如果在 html 中存在 <img> <link> 等标签,预加载扫描器会把这些请求传递给浏览器进程中的网络线程进行相关资源的下载。

构建DOM树

为什么浏览器要构建DOM树?这是因为浏览器无法直接理解和使用HTML文本,需要将HTML转换为浏览器能够理解的结构—DOM树。为了更加直观地理解DOM树,我们可以打开Chrome的“开发者工具”,选择“Console”标签来打开控制台,然后在控制台里面输入“document”后回车,就能看到一个完整的DOM树结构,如下图所示:

a95cf08d9006b3320dbc0159ad87b94c.png

DOM和HTML内容几乎是一样的,但是和HTML不同的是,DOM是保存在内存中树状结构,可以通过JavaScript来查询或修改其内容。

构建CSSOM

为什么浏览器要构建CSSOM树?原因和浏览器构建DOM树一样,也是无法直接理解这些纯文本的CSS样式,所以当渲染引擎接收到CSS文本时,会执行一个转换操作,将CSS文本转换为浏览器可以理解的结构——styleSheets。我们可以在Chrome控制台中查看其结构,只需要在控制台中输入document.styleSheets,然后就看到如下图所示的结构

efc281b18dbb63b04113dd7e9b7df91f.png

样式计算

渲染进程主线程计算每一个元素节点的最终样式值,即使没有任何CSS样式,浏览器对每个元素也会有一个默认的样式。我们也可以通过Chrome查看浏览器计算后的样式

5569856996d662ae4a54bea371c190b9.png

生成布局树

为了构建布局树Render tree,浏览器大体上完成了下面这些工作

  • 遍历DOM树中的所有可见节点,并把这些节点加到布局(Layout)中;
  • 而不可见的节点会被布局树忽略掉,如head标签下面的全部内容,再比如body.p.span这个元素,因为它的属性包含 dispaly:none,所以这个元素也没有被包进布局树。

08c1f2ed0b921c2737b24bf65a3cac7a.png

回到Event Log继续观察分析,如下图,js引擎在彻底加载执行完毕vendor.js,manifest.js,app.js后,渲染引擎回归,开始第三次解析Html,开始进行首次样式计算和布局,并动态加载manifest映射中的app-async.js等(项目webpack打包split chunk优化后的产物)和首页correlate-Hplan.js资源等。

97af291207396ae74a95e70840ab56a5.png

绘制

即使知道了不同元素的位置及样式信息,浏览器还需要知道不同元素的绘制先后顺序才能正确绘制出整个页面。在绘制阶段,主线程会遍历布局树创建待绘制列表。打开“开发者工具”的“Layers”标签,选择“document”层,来实际体验下绘制列表

79450d810f30bb71c01a8e5b49ddbec7.png

绘制过程可以将布局树中的元素分解为多个层。

回到performance面板,我们勾选上控制面板里的 Enable advanced paint instrumentation 记录渲染事件的细节:选择Frames中的一块,可在“Event log” 选项卡旁边的新“Layers”选项卡中显示有关页面分层的信息。

f8f098f4a8387b4ef8c0c1da612bb006.png
将内容提升到GPU上的层(而不是CPU上的主线程)可以提高绘制和重新绘制性能。有一些特定的属性和元素可以实例化一个层,包括<video><canvas>,任何CSS属性为opacity、3D转换、will-change的元素,还有一些其他元素。这些节点将与子节点一起绘制到它们自己的层上,除非子节点由于上述一个(或多个)原因需要自己的层。

层确实可以提高性能,但是它以内存管理为代价,因此不应作为web性能优化策略的一部分过度使用。

继续来观察这个阶段的Event Log的活动,刚刚分析到浏览器第三次解析html,动态加载完webpack拆分的chunk和首页js,js引擎执行完毕触发Event load事件,紧接着触发了domInteractive事件也标识了html解析结束,html解析器结束工作后触发docmument.readystatechange事件,紧接着触发DomContentLoaded事件后,更新布局树,进行首次绘制。

9226cec09dcd06f5133eaca1f497a4a4.png

这里我们再来理解下这个DomContentLoaded事件与Load事件的区别

HTML 页面的生命周期包含三个重要事件:
  • DOMContentLoaded —— 浏览器已完全加载 HTML,并构建了 DOM 树,但像 和样式表之类的外部资源可能尚未加载完成。
  • load —— 浏览器不仅加载完成了 HTML,还加载完成了所有外部资源:图片,样式等。
  • beforeunload/unload —— 当用户正在离开页面时。

我们来看Timings模块,可以看到DCL,FP,FCP,L,LCP几个重要的与页面性能优化密切相关的事件缩写,这里我们只理解下DCL(DOMContentLoaded)和L(load),load事件触发的时机明显晚于DOMContentLoaded

9f90184d8118a258645f38799a5bc181.png

282f5fd5a126c26ae4e1a0f608041126.png

合成与显示

一旦层树被创建,绘制列表被确定,主线程会把这些信息通知给合成器线程,合成器线程会栅格化每一层。有的层的可以达到整个页面的大小,因此,合成器线程将它们分成多个磁贴,并将每个磁贴发送到栅格线程,栅格线程会栅格化每一个磁贴并存储在 GPU 显存中。合成线程发送绘制图块命令DrawQuad给浏览器进程。浏览器进程根据DrawQuad消息生成页面,并显示到显示器上。

回到performance工具,这里,我们滑动缩小概览模块选中的区间,定位到页面骨架屏与初次渲染出页面的中间,在Event Log中只勾选Painting会自动筛选出区间范围内的绘制事件日志,可以看到浏览器进行了多次绘制和合成图层的操作。

fcf7c4fc313c9e17f25ad8acb4db08eb.png

待优化的性能问题

额,这部分本来没有在计划范围内 ,主要是两个问题被chrome⚠️了:

  • Long task多个主任务都花费了较长的时间被警告,主要是打包后js的体积大造成的
  • Layout Shift布局偏移过大,导致用户体验差,简单点理解就是你准备点一个链接或者按钮,但就在你手指触摸到屏幕的前一秒,链接移动了,你点到了其他地方

e170842ffe19c9e7087e2e69874544fc.png

总结

通过performance工具的Event Log模块,分析了一下转转某实际vue项目 SPA单页应用页面在浏览器中的渲染过程,过程中可能有理解不到位的地方,欢迎多多指出呀。

参考文献

[1].https://blog.poetries.top/browser-working-principle/guide/part1/lesson05.html

[2].https://developers.google.com/web/tools/chrome-devtools/evaluate-performance/reference

[3].https://developer.mozilla.org/zh-CN/docs/Web/Performance/%E6%B5%8F%E8%A7%88%E5%99%A8%E6%B8%B2%E6%9F%93%E9%A1%B5%E9%9D%A2%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86

[4].https://developers.google.com/web/updates/2018/09/inside-browser-part3

[5].https://zhuanlan.zhihu.com/p/41017888

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/337606.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机进管理提示找不到入口,win10系统开机提示xxxdll模块已加载但找不到入口点的教程...

有关win10系统开机提示xxxdll模块已加载但找不到入口点的操作方法想必大家有所耳闻。但是能够对win10系统开机提示xxxdll模块已加载但找不到入口点进行实际操作的人却不多。其实解决win10系统开机提示xxxdll模块已加载但找不到入口点的问题也不是难事&#xff0c;小编这里提示两…

十大经典排序,你真的都会了吗?(源码详解)

点击蓝字关注我们一、前言&#xff1a;排序的概念排序&#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。稳定性&#xff1a;假定在待排序的记录序列中&#xff0c;存在多个具有相同的关键…

jvm 架构_不可变的基础架构,热部署和JVM

jvm 架构您是否在生产中部署和取消部署基于JVM的应用程序&#xff08;无论JVM容器/无容器&#xff09;&#xff1f; 也就是说&#xff0c;当您拥有某个应用程序或服务的新版本时&#xff0c;是否通过“取消部署”和“热部署”该应用程序的新更新版本来更改正在运行的JVM&#x…

c语言默认参数_5.1 C++有默认参数的函数

点击上方“C语言入门到精通”&#xff0c;选择置顶第一时间关注程序猿身边的故事作者闫小林白天搬砖&#xff0c;晚上做梦。我有故事&#xff0c;你有酒么&#xff1f;C有默认参数的函数在函数调用时形参从实参获取值&#xff0c;因为实参的个数要和形参相同&#xff0c;但有时…

计算机组成原理唐朔飞课后答案第六章,计算机组成原理第六章部分课后题答案(唐朔飞版)...

计算机组成原理第六章部分课后题答案(唐朔飞版) 6.4 设机器数字‎长为8位(含1位符号‎位在内)&#xff0c;写出对应下‎列各真值的‎原码、补码和反码‎。 -13/64&#xff0c;29/128&#xff0c;100&#xff0c;-87 解&#xff1a;十进制数 二进制数 原 码 反 码 补 码 -13/64 …

jboss eap 7.0_是时候抛弃Java 7 – JBoss EAP 6.4了!

jboss eap 7.0这一周真是太棒了。 JBoss EAP 6.4已发布&#xff0c;在众多技术增强和新功能中 &#xff0c;最大的是&#xff1a;Java 8已添加到受支持的配置列表中。 其中包括Oracle JDK和IBM JDK。 Java SE 7公开更新结束通知 2015年4月之后&#xff0c;Oracle将不再将Java …

C语言史上最愚蠢的BUG ???

点击蓝字关注我们本文来自“The most stupid C bug ever”&#xff0c;很有意思&#xff0c;分享给大家。我相信这样的bug&#xff0c;就算你是高手你也会犯的。你来看看作者犯的这个Bug吧。。首先&#xff0c;作者想用一段程序来创建一个文件&#xff0c;如果有文件名的话&…

python 字符串转日期_我总结的130页Python与机器学习之路V1.2.pdf,都是干货!

告别枯燥&#xff0c;通过学习有趣的小例子&#xff0c;扎实而系统的入门Python&#xff0c;从菜鸟到大师&#xff0c;个人觉得这是很靠谱的一种方法。通过一个又一个的小例子&#xff0c;真正领悟Python之强大&#xff0c;之简洁&#xff0c;真正做到高效使用Python.两周前发出…

大学计算机需要论文吗,大一新生刚开学,是否有必要带电脑?听听辅导员的建议,非常中肯...

原标题&#xff1a;大一新生刚开学&#xff0c;是否有必要带电脑&#xff1f;听听辅导员的建议&#xff0c;非常中肯各大高校的录取工作正在如火如荼的进行&#xff0c;很快考生们就能接到来自各个学校的录取通知书。对于考生来说&#xff0c;没有什么事情会比被心仪的大学录取…

计算机系统是连续系统,连续系统的计算机模拟

连续系统的计算机模拟 (36页)本资源提供全文预览&#xff0c;点击全文预览即可全文预览,如果喜欢文档就下载吧&#xff0c;查找使用更方便哦&#xff01;29.9 积分&#xfeff; 第2章 连续系统的计算机模拟本章讨论连续系统的模拟技术,由于这类系统中状态随时间连续动态地变化&…

ae合成设置快捷键_教程|AE教程第三波:必须掌握的关键帧之基础设置

该如何高效的学习AE&#xff1f;星驰君认为知其然还需知其所以然正确的学习顺序应该是了解核心原理&#xff0c;掌握基本操作&#xff0c;案例实战模仿比如&#xff0c;想要更好的掌握和运用关键帧来制作更复杂的效果。就先要知道关键帧是什么关键帧&#xff1a;计算机动画术语…

老兵精讲:高质量C语言编程的10条规范

点击蓝字关注我们C语言编码规范10条分享给大家&#xff0c;还是可以规避掉很多bug的&#xff01;1、最重要的规则编写代码时最重要的一条规则是&#xff1a;检查周围的代码并尝试模仿它。作为维护人员&#xff0c;如果收到的补丁明显与周围代码的编码风格不同&#xff0c;这是令…

springboot需要tomcat服务器吗_SpringBoot学习(二):内嵌服务器引擎(Tomcat,Jetty)实现原理...

概述SpringBoot使用main方法启动的一个重要特性是&#xff0c;不需要打包成war部署到Tomcat这种Servlet容器中&#xff0c;而是只需打包成jar&#xff0c;然后通过java或mvn等命令运行这个jar包&#xff0c;然后应用就可以在指定的端口监听客户端的连接请求了。在SpringBoot内部…

java8 函数式编程_使用Javaslang进行Java 8中的函数式编程

java8 函数式编程我们非常高兴地在jOOQ博客上宣布一个客座帖子&#xff0c;该帖子由HSH Nordbank的高级软件工程师Daniel Dietrich &#xff08;三人的丈夫和父亲&#xff09;撰写。 他目前作为项目负责人和首席开发人员为金融产品创建定价框架。 除工作外&#xff0c;他还对编…

while(1) 和 for(;;)有什么区别?

点击蓝字关注我们有粉丝问了类似这样的问题&#xff1a;while(1) 和 for(;;)它们不都是无限循环吗&#xff0c;作用应该一样啊&#xff0c;它们到底有什么区别&#xff1f;要回答这个问题&#xff0c;其实你各自编写一段while(1) 和 for(;;)的代码&#xff0c;编译对比一下代码…

C++高阶必会操作--模板元编程

点击蓝字关注我们泛型编程大家应该都很熟悉了&#xff0c;主要就是利用模板实现“安全的宏”&#xff0c;而模板元编程区别于我们所知道的泛型编程&#xff0c;它是一种较为复杂的模板&#xff0c;属于C的高阶操作了&#xff0c;它最主要的优点就在于把计算过程提前到编译期&am…

怎样做远程计算机控制系统,qq远程控制,怎样进行远程控制制作步骤

怎样设置qq远程控制&#xff1f;qq上有一个远程协助不少人都用过&#xff0c;但是这个远程小编建议不是认识的朋友&#xff0c;不要随便开放&#xff0c;很容易被窃取电脑资料&#xff0c;如果是不是很信得过有人&#xff0c;在申请协助时一定要自己盯着电脑&#xff0c;以防别…

pcl中ransac提取直线_复杂场景中的一个图像配准思路

在很多时候&#xff0c;我们可能需要使用到图像的识别与配准工作&#xff0c;来判断某个特征或者是划出某个特定特征的位置。现在的深度学习已经能够比较好地解决这个问题&#xff0c;比如常见的YOLO&#xff0c;可以利用几行设定代码就能够划出所需要识别的位置。但是精准度可…

程序员必知的10个C语言技巧

点击蓝字关注我们硬件设计师最常见的工作内容&#xff0c;就是通过写代码来测试硬件。这10个C语言技巧&#xff08;C语言仍然是常见的选择&#xff09;可以帮助设计师避免因基础性错误而导致某些缺陷的产生&#xff0c;并造成维护方面的困扰。为了成功的推出一个产品&#xff0…

一万字详解C语言中长度为零的数组

点击蓝字关注我们零长度数组概念&#xff1a;众所周知, GNU/GCC 在标准的 C/C 基础上做了有实用性的扩展, 零长度数组&#xff08;Arrays of Length Zero&#xff09; 就是其中一个知名的扩展.多数情况下, 其应用在变长数组中, 其定义如下&#xff1a;struct Packet {int state…