关闭浏览器网页触发事件_浅析浏览器渲染和 script 加载
前言
前端代码离不开浏览器环境,理解 js、css 代码如何在浏览器中工作是非常重要的。
如何优化渲染过程中的回流,重绘?script 脚本在页面中是怎么个加载顺序?了解这些对前端性能优化起着非常大的作用。
借着这篇文章,让自己对这块知识的理解更深一步。
渲染
渲染树(Render Tree)
浏览器通过解析 HTML 和 CSS 后,形成对应的 DOM 树和 CSSOM 树。
从根节点开始解析 DOM 树节点并匹配对应的 CSSOM 样式规则,选择可见的的节点,最终结合成一颗渲染树。
从上图能看到渲染树的特点:
- 渲染树中不包含 head、script、link、meta 之类不可见的节点
- CSS 定义的样式规则将和实际的 DOM 匹配,并且被 display:none 修饰的节点最终不会出现在渲染树中
渲染阶段
根据上图,整个渲染阶段分为三部分:
- 渲染树的形成:通过 DOM 和 CSSOM 形成渲染树
- 布局 Layout(自动重排 Reflow):基于页面的流式布局,遍历渲染树节点,不断计算节点最终的位置,几何信息,样式等属性后,输出一个“盒模型”
- 绘制 Paint(栅格化):将节点位置,大小根据屏幕的窗口大小换算成真实的像素,同颜色等属性一同“画到”页面上
回流和重绘
基本概念
- 回流 Reflow:某些元素位置、几何形状的更改需要浏览器重新计算相关元素。
- 重绘 Repaint:将回流重排好的元素绘制到页面上,但也因某些 js、css 的修改导致渲染树发生变化,浏览器需要再次绘制页面。
两者的关系:触发回流一定会触发重绘, 而触发重绘却不一定会触发回流
下图很形象的展示了 Mozilla 页面的渲染过程。
触发回流条件
- 首次布局渲染页面
- 改变浏览器窗口大小
- 改变字体
- 网页内容变化
- 触发 CSS 伪类
- 操作 DOM
- style 样式表发生变化
- 调用 DOM 元素的 offsetXX, clientXX,scrollXX,getClientRects 等属性方法,获取元素当前的位置度量信息(参见)
如何测试网页性能
都知道频繁的渲染过程会影响网页性能,但怎么知道网页开始渲染内容了呢?
我们可以通过 Chrome 的 F12,选择 Rendering 来查看网页的性能。
- Paint flashing: 以绿色高亮重绘区域
- Layout Shift Regions: 以蓝色高亮布局发生变化的区域
结合上面的方法,用 一个简单的 Demo 来示意:
能从图中看到,这些操作 触发了浏览器的重绘:
- 鼠标移至按钮上,触发了默认的 hover 效果(出现绿框)
- 改变元素 color 属性(出现绿框)
- 修改元素 top 属性,不断改变元素位置影响布局(出现绿框,蓝框)
提升渲染性能
布局/回流 和 绘制/重绘 是页面渲染必须会经过的两个过程,不断触发它们肯定会增加性能的消耗。
浏览器会对这些操作做优化(把它们放到一个队列,批量进行操作),但如果我们调用上面提到的 offsetXX, clientXX,scrollXX,getClientRects 等属性方法就会强制刷新这个队列,导致这些队列批量优化无效。
下面列举一些简单优化方式:
- 不要使用 table 布局 table 布局会破坏 HTML 流式解析过程,甚至内部元素改动会触发整个 table 重绘
- 将需更改的 class 放到最里层 明确元素位置,减少父类元素不必要渲染判断
- 使用 fixed、absolute 属性修饰复杂多变的处理(动画) 将改变范围降到最低程度,避免影响到父级元素
- 合并,减少 DOM 操作;通过虚拟 DOM 来代替
脚本的加载
link 和 script 加载文件的差异
注:均放在 head 标签内。
考个问题:CSS 定义在 head 中,其需加载 5 秒,请问页面加载后内容会先优先展示吗?
我被渲染出来了
我原先以为页面内容会优先渲染,CSS 加载完成后才改变内容样式。其实这是错的。
从上图看到,页面加载后,body 内元素就已经解析好了,只是没有渲染到页面上。随后 CSS 文件加载后,带有样色的内容才被渲染到页面上。
延迟的 link 的加载阻断了页面渲染,但并没有影响 HTML 的解析,当 CSS 加载后,DOM 完成解析,CSSOM 和 DOM 形成渲染树,最后将内容渲染到页面上。
反问,将 link 替换成 script 效果也一样吗?
与 link 不同,script 的加载会阻断页面 HTML 的解析,浏览器解析完 script 后,会等待 js 文件加载完后,页面才开始后续的解析,body 内容才出现。
head 和 body 中的 script 标签
学前端时相信都听过这样的名言:
CSS 写在 head 里,js 写在 body 结束标签前
知道了上面 link 和 script 的区别后,应该明白前半句的含义,下面来解释下后半句。
下面 script 均在 body 中。
页面渲染 和 script 加载
先看下脚本在 body 中的一般情况:
在 body 内部的首位分别加载两个 js 文件,前者延迟 3 秒,后者延迟 5 秒,为了清楚他们的“工作”情况,在 head 中添加了定时器示意。
我被渲染了
能看到 body 中定义的内联脚本首先工作,初始化 foo 变量。
随后加载 addTen.js,并阻断页面渲染。3 秒后,输出 js 内容(foo 赋值为 10),页面并重新开始解析,展示 div 内容。
最后加载 addOne.js ,继续等待 2 秒后,输出 js 内容(foo 赋值为 11)。
多个 script 文件的加载
如果前一个 js 文件加载慢于后一个,会有怎么个效果?
我被渲染了
两个 script 标签并行加载,1 秒后 addOne.js 首先加载完毕,等待 4s 秒后,addTen.js 加载完后,页面直接渲染(因为 script 已经全部完成)。
简单总结下
- 无论在 head 还是 body 中,浏览器会等待 script 文件的加载(阻断页面解析渲染)
- 多个 script 的文件加载是异步的,不存在互相影响(后一个文件不需要等待前一个加载完后才下载),执行顺序同定义顺序
所以建议 script 放在 body 结束标签之前,确保页面内容全部解析完成并开始渲染。
DOM 的 DOMContentLoaded 事件
DOMContentLoaded 事件可以来确定整个 DOM 是否全部加载完成,下面我们简单测试下:
我被渲染了
最终输出:
addTen.jsfoo 10addOne.jsfoo 11[ready] document
DOMContentLoaded 事件的定义是异步回调方式,当 DOM 加载完成后触发,即使写在最前面,也会等待后面的 script 加载完成后才触发。
这里顺便提个 window.onload :
window.onload 和 DOMContentLoaded 不同,前者会等待页面中所有的资源加载完毕后再调用执行(比如:img 标签),后者在 DOM 加载完毕后即触发。
“真正的异步脚本”——动态脚本
能看到无论 script 放在那个位置,浏览器都会等待他们直至 body 内的文件全部加载完。
那有什么 真正的异步 脚本加载吗?(不会阻断页面解析)
那就是 动态脚本。
如果你接触过第三方网页统计脚本,那将比较了解,下面给段示例代码:
我被渲染了
最终输出:
addTen.jsafoo 10addOne.jsfoo 11[ready] document已加载 5 秒已加载 6 秒已加载 7 秒已加载 8 秒dynamicScript.js is runningdynamicScript.js loaded已加载 9 秒已加载 10 秒
定义了需要加载 8 秒的 dynamicScript.js 文件,所有的 script 加载方式依旧异步,但 dynamicScript.js 在 DOMContentLoaded 触发后,最后才执行,浏览器并没有等待它的加载完成后才渲染页面。
我们也可以将它放在 head 中。这种通过脚本来动态修改 DOM 结构的加载方式是 无阻塞式 的,不受其他脚本加载的影响。
defer 和 async
我们可以在 script 定义 defer 、 async ,使整个脚本加载方式更加友好。比如:被修饰的脚本在 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 排版
关闭浏览器网页触发事件_浅析浏览器渲染和 script 加载相关推荐
- 关闭浏览器网页触发事件_浏览器是如何工作的?
作者:zhangwang 原文链接:https://zhuanlan.zhihu.com/p/47407398 可能每一个前端工程师都想要理解浏览器的工作原理. 我们希望知道从在浏览器地址栏中输入 u ...
- ie浏览器网页版进入_荟萃浏览器v2.10.2清爽版 网页秒开/装机必备
荟萃浏览器v2.10.2清爽版 网页秒开/装机必备 https://www.lanzoux.com/iEq2ge0s3ej ·简约风格 界面简洁布局清爽,感受纯净简约之美. ·极速浏览 精心优化加载速 ...
- 计算机如何快速关闭网络页面,关闭浏览器快捷键是什么_电脑浏览器快捷键怎么关闭-win7之家...
对于电脑中经常使用的程序,无疑就是属于浏览器了,大部分用户都会通过浏览器来打开一些网页,然而由于打开的窗口过多,每次想要关闭时,都需要进行确认操作,很是麻烦,那么电脑浏览器快捷键怎么关闭呢?接下来小编 ...
- java弹窗 触发事件_关于ElementUI中MessageBox弹框的取消键盘触发事件(enter,esc)关闭弹窗(执行事件)的解决方法...
好久没见了 在项目中遇到一个小小的需求,总结了一下! 详细我就不介绍了,相信大家用过的话,很了解.详见文档-----------> http://element-cn.eleme.io/#/zh ...
- js hover 触发事件_为什么说JS的DOM操作很耗性能
想问这样的问题,其实是自己心中没有个谱,一直用 js 计算性能来衡量 浏览器dom 操作性能.js性能和浏览器性能其实是两码事. 这个问题很抽象,它里面涉及挺多个小的知识点. 重申一点,js 操作 D ...
- JS关闭窗口时触发事件方法
JS监听关闭浏览器事件 1.Onunload与Onbeforeunload区别: ①Onunload,onbeforeunload都是在刷新或关闭时调用,可以在 ②Onbeforeunload也是在页 ...
- ie浏览器开发比谷歌浏览器_跨浏览器开发:处理IE
ie浏览器开发比谷歌浏览器 I compare developing for Internet Explorer to growing up in a mental asylum. If you we ...
- JS在即将离开当前页面(刷新或关闭)时触发事件
// onbeforeunload 事件在即将离开当前页面(刷新或关闭)时触发window.onbeforeunload = function () {return /^\#\/ipinfo/.tes ...
- 监听浏览器的返回事件,禁止浏览器返回
//禁止页面后退 history.pushState(null,null,document.URL); window.addEventListener('popstate',function(){// ...
最新文章
- Paddle 网络中的Tensor 数据结构
- vue计算属性computed与监听属性watch的基本使用
- 判断系统是大端还是小段
- power bi函数_在Power BI中的行上使用聚合函数
- 编写第一个Java程序:helloworld
- 20155335俞昆《java程序设计》第十周总结
- 详解nullable、firstpos、lastpos和followpos的计算规则
- 程序员给小姐姐修电脑的最佳地方和姿势
- *第六周*数据结构实践项目三【括号的配对】
- 直线电机原理动画_直线振动筛工作原理结构图以及结构解析
- 一段关于中国人口老龄化的评论
- 做Meta分析要用哪些软件?Meta分析软件盘点,含软件安装包!
- VLD(Visual LeakDetector)内存泄露库的使用
- 操作系统介绍,为什么使用虚拟机,虚拟机使用原理透彻解释
- 新世纪大学英语(第二版)综合教程第一册 Unit 1 (中英翻译和重点单词)
- AutoCAD中如何截取清楚的图像
- 行式数据和列式数据对比 存储压缩性能
- OneNote 与 OneNote 2016 有什么区别?
- ScyllaDB 1.2 国内安装更新源发布
- 基于基站定位数据的商圈分析
热门文章
- Java与C#个人之比较
- 公司管理项目管理中的技巧
- 跨域资源共享 CORS 详解
- 解决:java.lang.IllegalStateException: ApplicationEventMulticaster not initialized
- Spring之JDBCTemplate
- dependency 中的 classifier属性
- 关于RESTful一些注意事项,接口开发规范
- jrebel、JavaRebel
- java.util.UnknownFormatConversionException: Conversion = ‘,‘ 解决
- 2019-03-28 SQL Server Pivot