关闭浏览器网页触发事件_浅析浏览器渲染和 script 加载

5a2303deaad679ac776d9a811bb0df1b.png

前言

前端代码离不开浏览器环境,理解 js、css 代码如何在浏览器中工作是非常重要的。

如何优化渲染过程中的回流,重绘?script 脚本在页面中是怎么个加载顺序?了解这些对前端性能优化起着非常大的作用。

借着这篇文章,让自己对这块知识的理解更深一步。

渲染

渲染树(Render Tree)

浏览器通过解析 HTML 和 CSS 后,形成对应的 DOM 树和 CSSOM 树。

从根节点开始解析 DOM 树节点并匹配对应的 CSSOM 样式规则,选择可见的的节点,最终结合成一颗渲染树

e6bc3e8f0a7ad266d6bd3f2b4280e86d.png

从上图能看到渲染树的特点:

  • 渲染树中不包含 head、script、link、meta 之类不可见的节点
  • CSS 定义的样式规则将和实际的 DOM 匹配,并且被 display:none 修饰的节点最终不会出现在渲染树中

渲染阶段

8dcc8d4bac16596dbf1ffffe8b76db42.png

根据上图,整个渲染阶段分为三部分:

  • 渲染树的形成:通过 DOM 和 CSSOM 形成渲染树
  • 布局 Layout(自动重排 Reflow):基于页面的流式布局,遍历渲染树节点,不断计算节点最终的位置,几何信息,样式等属性后,输出一个“盒模型”
  • 绘制 Paint(栅格化):将节点位置,大小根据屏幕的窗口大小换算成真实的像素,同颜色等属性一同“画到”页面上

回流和重绘

基本概念

  • 回流 Reflow:某些元素位置、几何形状的更改需要浏览器重新计算相关元素。
  • 重绘 Repaint:将回流重排好的元素绘制到页面上,但也因某些 js、css 的修改导致渲染树发生变化,浏览器需要再次绘制页面。

两者的关系:触发回流一定会触发重绘, 而触发重绘却不一定会触发回流

下图很形象的展示了 Mozilla 页面的渲染过程。

dcdfbb214ab35f3a554b5f94f476e408.gif

触发回流条件

  • 首次布局渲染页面
  • 改变浏览器窗口大小
  • 改变字体
  • 网页内容变化
  • 触发 CSS 伪类
  • 操作 DOM
  • style 样式表发生变化
  • 调用 DOM 元素的 offsetXX, clientXX,scrollXX,getClientRects 等属性方法,获取元素当前的位置度量信息(参见)

如何测试网页性能

都知道频繁的渲染过程会影响网页性能,但怎么知道网页开始渲染内容了呢?

我们可以通过 Chrome 的 F12,选择 Rendering 来查看网页的性能。

cadb0ab9fbb76bd77ee345da1d603242.png
58007af69f8c0795b058735812c57f59.png
  • Paint flashing: 以绿色高亮重绘区域
  • Layout Shift Regions: 以蓝色高亮布局发生变化的区域

结合上面的方法,用 一个简单的 Demo 来示意:

5d102ca3e7b9669dd7786d3b2708c95e.gif

能从图中看到,这些操作 触发了浏览器的重绘

  • 鼠标移至按钮上,触发了默认的 hover 效果(出现绿框)
  • 改变元素 color 属性(出现绿框)
  • 修改元素 top 属性,不断改变元素位置影响布局(出现绿框,蓝框)

提升渲染性能

布局/回流绘制/重绘 是页面渲染必须会经过的两个过程,不断触发它们肯定会增加性能的消耗。

浏览器会对这些操作做优化(把它们放到一个队列,批量进行操作),但如果我们调用上面提到的 offsetXX, clientXX,scrollXX,getClientRects 等属性方法就会强制刷新这个队列,导致这些队列批量优化无效。

下面列举一些简单优化方式:

  • 不要使用 table 布局 table 布局会破坏 HTML 流式解析过程,甚至内部元素改动会触发整个 table 重绘
  • 将需更改的 class 放到最里层 明确元素位置,减少父类元素不必要渲染判断
  • 使用 fixed、absolute 属性修饰复杂多变的处理(动画) 将改变范围降到最低程度,避免影响到父级元素
  • 合并,减少 DOM 操作;通过虚拟 DOM 来代替

脚本的加载

link 和 script 加载文件的差异

注:均放在 head 标签内。

考个问题:CSS 定义在 head 中,其需加载 5 秒,请问页面加载后内容会先优先展示吗?

          
我被渲染出来了

我原先以为页面内容会优先渲染,CSS 加载完成后才改变内容样式。其实这是错的。

ec655f5d353fcd6f94e72e9b5acc469a.gif

从上图看到,页面加载后,body 内元素就已经解析好了,只是没有渲染到页面上。随后 CSS 文件加载后,带有样色的内容才被渲染到页面上。

延迟的 link 的加载阻断了页面渲染,但并没有影响 HTML 的解析,当 CSS 加载后,DOM 完成解析,CSSOM 和 DOM 形成渲染树,最后将内容渲染到页面上。

反问,将 link 替换成 script 效果也一样吗?

a2bfb539740d7610d9a68d6732147bd3.gif

与 link 不同,script 的加载会阻断页面 HTML 的解析,浏览器解析完 script 后,会等待 js 文件加载完后,页面才开始后续的解析,body 内容才出现。

head 和 body 中的 script 标签

学前端时相信都听过这样的名言:

CSS 写在 head 里,js 写在 body 结束标签前

知道了上面 link 和 script 的区别后,应该明白前半句的含义,下面来解释下后半句。

下面 script 均在 body 中

页面渲染 和 script 加载

先看下脚本在 body 中的一般情况:

在 body 内部的首位分别加载两个 js 文件,前者延迟 3 秒,后者延迟 5 秒,为了清楚他们的“工作”情况,在 head 中添加了定时器示意。

                      
我被渲染了
6767c0f66ed6c6812e9acee88fd5db4c.gif

能看到 body 中定义的内联脚本首先工作,初始化 foo 变量。

随后加载 addTen.js,并阻断页面渲染。3 秒后,输出 js 内容(foo 赋值为 10),页面并重新开始解析,展示 div 内容。

最后加载 addOne.js ,继续等待 2 秒后,输出 js 内容(foo 赋值为 11)。

340572169c396f7a46fd995cad51d650.png

多个 script 文件的加载

如果前一个 js 文件加载慢于后一个,会有怎么个效果?

我被渲染了

两个 script 标签并行加载,1 秒后 addOne.js 首先加载完毕,等待 4s 秒后,addTen.js 加载完后,页面直接渲染(因为 script 已经全部完成)。

aabac8bbee4e39d478b8d8e230b46016.png

简单总结下

  1. 无论在 head 还是 body 中,浏览器会等待 script 文件的加载(阻断页面解析渲染)
  2. 多个 script 的文件加载是异步的,不存在互相影响(后一个文件不需要等待前一个加载完后才下载),执行顺序同定义顺序

所以建议 script 放在 body 结束标签之前,确保页面内容全部解析完成并开始渲染。

DOM 的 DOMContentLoaded 事件

DOMContentLoaded 事件可以来确定整个 DOM 是否全部加载完成,下面我们简单测试下:

我被渲染了

最终输出:

addTen.jsfoo 10addOne.jsfoo 11[ready] document

DOMContentLoaded 事件的定义是异步回调方式,当 DOM 加载完成后触发,即使写在最前面,也会等待后面的 script 加载完成后才触发。

这里顺便提个 window.onload

window.onloadDOMContentLoaded 不同,前者会等待页面中所有的资源加载完毕后再调用执行(比如:img 标签),后者在 DOM 加载完毕后即触发。

“真正的异步脚本”——动态脚本

能看到无论 script 放在那个位置,浏览器都会等待他们直至 body 内的文件全部加载完。

那有什么 真正的异步 脚本加载吗?(不会阻断页面解析)

那就是 动态脚本

如果你接触过第三方网页统计脚本,那将比较了解,下面给段示例代码:

我被渲染了

最终输出:

addTen.jsafoo 10addOne.jsfoo 11[ready] document已加载  5  秒已加载  6  秒已加载  7  秒已加载  8  秒dynamicScript.js is runningdynamicScript.js loaded已加载  9  秒已加载  10  秒
668e619bffa63684a22df20150441af2.png

定义了需要加载 8 秒的 dynamicScript.js 文件,所有的 script 加载方式依旧异步,但 dynamicScript.js 在 DOMContentLoaded 触发后,最后才执行,浏览器并没有等待它的加载完成后才渲染页面。

我们也可以将它放在 head 中。这种通过脚本来动态修改 DOM 结构的加载方式是 无阻塞式 的,不受其他脚本加载的影响。

defer 和 async

我们可以在 script 定义 deferasync ,使整个脚本加载方式更加友好。比如:被修饰的脚本在 head 中,将不会阻断 body 内容的展示

注意: defer 修饰的脚本将延迟到 body 中所有定义的脚本之后,DOM(页面内容)加载完之前触发async 不会像 defer 一样等待 body 中的脚本,而是当前脚本一加载完毕就触发。

                  
我被渲染了

加载顺序:

已加载  1  秒已加载  2  秒scriptAsync.js已加载  3  秒已加载  4  秒addTen.jsfoo 10addOne.jsfoo 11scriptDefer.js[ready] document已加载  5  秒已加载  6  秒已加载  7  秒已加载  8  秒dynamicScript.js is runningdynamicScript.js loaded已加载  9  秒已加载  10  秒

本文使用 mdnice 排版

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

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

相关文章

Open vSwitch实验常用命令

1. 基本架构 ovs-vsctl: 管理ovsdb-server的配置,提供OVSDB的配置方法,包括创建和删除网桥、端口等; ovs-ofctl: 提供ovs-vswitchd的流表配置方法; ovs-dpctl: 配置OVS内核模块,提供缓存流表的操作方法&#xff1b…

Oracle 存储过程错误之PLS-00201: 必须声明标识符

转自:http://blog.csdn.net/u010678947/article/details/20702149 错误: ORA-06550: 第 1 行, 第 7 列: PLS-00201: 必须声明标识符ZUO.PROCE_TESTORA-06550: 第 1 行, 第 7 列: PL/SQL: Statement ignored 解决方法: (1&#x…

mysql中如何把两个查询结果列数不同并成一张表_MySQL

引言本文整理了MySQL相关的知识,方便以后查阅。 基础架构下图是 MySQL 的一个简要架构图,从下图你可以很清晰的看到用户的 SQL 语句在 MySQL 内部是如何执行的。 先简单介绍一下下图涉及的一些组件的基本作用帮助大家理解这幅图。 - 连接器: …

scrapy框架_Python学习之Scrapy框架

爬虫界江湖地位No.1说起Python,不得不说到它的爬虫应用,由于Python的短小精悍,用它来开发爬虫应用是最合适不过了,基于Python抓取网页的库有很多,例如requests,beatifulsoup等等,但是要说到有哪一个框架&am…

cad移动时捕捉不到基点_CAD入门必备(一)移动和复制新手必看

cad也疯狂前言:CAD绘图之所以能够取代手工绘图,很大的一部分原因是因为它可以很方便的修改和重复利用,例如外参可以节省很大部分时间。而我们在使用CAD中,用得最频繁的功能就是移动和复制了,当然这也是新手必备的其中一…

H.264软件解码器在PXA270平台上的优化

罗 嵘,何 苦 时间:2009年04月24日摘 要: 研究了嵌入式系统中H.264 Baseline软件解码器设计和优化的问题,提出了四种有效的优化方法,并在PXA270平台上进行了测试。测试结果显示,综合使用提出的四种方法,H.26…

EF架构~codeFirst从初始化到数据库迁移

一些介绍 CodeFirst是EntityFrameworks的一种开发模式,即代码优先,它以业务代码为主,通过代码来生成数据库,并且加上migration的强大数据表比对功能来生成数据库版本,让程序开发人员不用维护数据库的变更,而…

lisp 车位块自动编号_机械车位做产权登记,真的适合吗?

为了更好地把握停车市场发展动向,给停车行业从业者提供一个发表观点、各抒己见的平台,共同促进停车行业的发展,《城市停车》开设热点版块,每期针对1-2个行业热点,广泛征集业内人士观点和看法。HOT TOPIC本期热点今年两…

webserver接口_SpringBoot内置源码解析WebServer初始化过程

WebServer 初始化过程在上一节中 Spring Boot 初始化了 WebServer 对应的工厂类。同时,我们也知道对应 Web容器的WebServer实现类有:TomcatWebServer、JettyWebServer和UndertowWebServer。这节重点讲解这些 WebServer 是如何被初始化,又如何启动的。Web…

提升应用程序弹性:保障工作负载正常运行

通过集群化、复制、快照、微服务和应用程序设计来提高企业工作负载的应用程序弹性和可用性。 应用程序的弹性和可用性是现代企业工作负载的关键属性。应用程序需要在硬件故障发生后,扛过服务故障(例如负载平衡器和域名系统错误)保持工作状态,并且可以忍受…

NVDKC6416平台H.264算法优化

本文转载自:http://blog.csdn.net/embedesign/archive/2009/09/15/4556486.aspx,版权归原作者,编辑:小乙哥 多媒体通信终端设备具有广泛的应用前景,可以应用于视频会议、可视电话、PDA、数字电视等各个领域&#xff0…

高德地图轨迹回放_高德地图上线了一个新功能….

文、路人甲TM德地图这两天刚上线了一个叫做「家人地图」的功能,所谓家人地图顾名思义,就是你可以通过高德地图组建一个家人圈,在这个圈子里面你可以看到你的家人在什么位置,当你的家人到达什么位置的时候自动发送通知或者警告&…

You have new mail in /var/spool/mail/root消除提示的方法

有时在进入系统的时候经常提示You have new mail in /var/spool/mail/root 你觉得烦人---解决方法: 修改系统配置文件/etc/profile,告诉系统不要去检查邮箱. 具体操作:命令行输入:echo "unset MAILCHECK" >> /etc…

Spring整合Quartz定时任务 在集群、分布式系统中的应用(Mysql数据库环境)

转载:http://www.cnblogs.com/jiafuwei/p/6145280.html 单个Quartz实例能给予你很好的Job调度能力,但它不能满足典型的企业需求,如可伸缩性、高可靠性满足。假如你需要故障转移的能力并能运行日益增多的 Job,Quartz集群势必成为你…

20温控f1什么意思_欧姆龙温控器是什么 欧姆龙温控器介绍【图文】

欧姆龙温控器,乍一眼看上去真的很难理解这到底是一个什么产品,或者是一个有什么用处的温控器,对于这个比较浅显的问题,不知道大家会有什么样地感受,是不是正在一头雾水的等着我进行解答呢?经过我比较浅显的分析&#…

zabbix3.2学习笔记(二):服务端安装

2019独角兽企业重金招聘Python工程师标准>>> 一般小公司大多将zabbix web端和zabbix server部署在同一台主机上,其实二者是可以分开的,web GUI配置连接到对应的数据库就行,让zabbix server和MySQL数据库在同一台主机上便于数据快速…

H.264解码器中CAVLC码表查找算法的分析与优化

0 引言 近年来,随着信息技术飞速发展和互联网的日益普及,尤其是以视频为信息主要来源的多媒体领域越来越受到人们的关注。H.264是ITU-T的视频编码专家组(VCEG)和ISO/IEC的活动图像编码专家组(MPEG)的联合视频组(Joint Video Te…

mp4文件格式系列

mp4文件格式系列1 - 综述Overview and Introduction Core Concepts MP4文件格式中,所有的内容存在一个称为movie的容器中。一个movie可以由多个tracks组成。每个track就是一个随时间变化的媒体序列,例如,视频帧序列。track里的每个时间单…

局域网网络风暴检测工具_【思唯网络学院】从原理到配置,最全的VLAN说明就在这了!...

有关VLAN的技术标准IEEE 802.1Q早在1999年6月份就由IEEE委员正式颁布实施了,而且最早的VLNA技术早在1996年Cisco(思科)公司就提出了。随着几年来的发展,VLAN技术得到广泛的支持,在大大小小的企业网络中广泛应用,成为当前最为热门的…

SQL server 基本语句

--查询数据库是否存在 if exists ( select * from sysdatabases where [name]TestDB) print Yes, the DB exists else print No, need a new one? --新建一个数据库 create database TestDB on ( name TestData, filename G:\DBS\KeyTest.mdf, size 3, filegrowth 2 ) log…