Products
GG网络技术分享 2025-03-18 16:14 0
我的想法:如果我要构建快速可靠的网站,需要真正了解浏览器渲染网页的每个步骤机制,这样就可以在开发过程中对每个步骤进行优化。这篇文章是我在较高水平上对端到端过程的学习总结。
好了,废话不多说,我们开始吧。这个过程可以分为以下几个主要阶段:
当浏览器通过网络接收页面的HTML数据时,它会立即设置解析器将HTML转换为文档对象模型(DOM)。
解析过程的第一步是将HTML分解并表示为开始标记、结束标记及其内容标记,然后它可以构造DOM。
当解析器遇到外部资源(如CSS或JavaScript文件)时,解析器将提取这些文件。解析器在加载CSS文件时继续运行,此时会阻止页面渲染,直到资源加载解析完(稍后会详细介绍)。
JavaScript 文件略有不同-默认情况下,解析器会在加载 JS 文件然后进行解析同时会阻止对HTML的解析。可以将两个属性添加到脚本标签中以减轻这种情况:defer 和async。两者都允许解析器在后台加载JavaScript 文件的同时继续运行,但是它们的执行方式不同。关于这一点后面还会再讲一点,但总的来说:
defer表示文件的执行将被延迟,直到文档的解析完成为止。如果多个文件具有defer属性,则将按照页面放置的顺序依次执行。
<scripttype="text/javascript"src="script.js"defer>
async 意味着文件将在加载后立即执行,这可能是在解析过程中或在解析过程之后执行的,因此不能保证异步脚本的执行顺序。
<scripttype="text/javascript"src="script.js"async>
<link> 元素的 rel 属性的属性值preload能够让你在你的HTML页面中 <head>元素内部书写一些声明式的资源获取请求,可以指明哪些资源是在页面加载完成后即刻需要的。
对于这种即刻需要的资源,你可能希望在页面加载的生命周期的早期阶段就开始获取,在浏览器的主渲染机制介入前就进行预加载。这一机制使得资源可以更早的得到加载并可用,且更不易阻塞页面的初步渲染,进而提升性能。
<linkhref="style.css"rel="preload"as="style"/>
你可能很早就知道DOM,但对**CSSOM(CSS对象模型)**可能听得少,反正我也没听过几次。
与HTML文件和DOM相似,加载CSS文件时,必须将它们解析并转换为树-这次是CSSOM。它描述了页面上的所有CSS选择器,它们的层次结构和属性。
CSSOM 与 DOM的不同之处在于它不能以增量方式构建,因为CSS规则由于特定性而可以在各个不同的点相互覆盖。这就是CSS 阻塞渲染的原因,因为在解析所有CSS并构建CSSOM之前,浏览器无法知道每个元素在屏幕上的位置。
不同的浏览器有不同的 JS 引擎来执行此任务。从计算机资源的角度来看,解析 JS 可能是一个昂贵的过程,比其他类型的资源更昂贵,因此优化它对于获得良好的性能是如此重要。
加载的JS和DOM被完全解析并准备就绪后就会 emit document.DOMContentLoaded事件。对于需要访问DOM的任何脚本,例如以某种方式进行操作或侦听用户交互事件,优良作法是在执行脚本之前先等待此事件。
document.addEventListener('DOMContentLoaded',(event)=>{
//这里面可以安全地访问DOM了
});
在所有其他内容(例如异步JavaScript,图像等)完成加载后,将触发window.load事件。
window.addEventListener('load',(event)=>{
//页面现已完全加载
});
渲染树是DOM和CSSOM的组合,表示将要渲染到页面上的所有内容。这并不一定意味着渲染树中的所有节点都将在视觉上呈现,例如,将包含opacity: 0或visibility: hidden的样式的节点,并仍然可以被屏幕阅读器等读取,而display: none不包括任何内容。此外,诸如<head>之类的不包含任何视觉信息的标签将始终被忽略。
与 JS 引擎一样,不同的浏览器具有不同的渲染引擎。
现在我们有了完整的渲染树,浏览器知道了要渲染什么,但是不知道在哪里渲染。因此,必须计算页面的布局(即每个节点的位置和大小)。渲染引擎从顶部开始一直向下遍历渲染树,计算应显示每个节点的坐标。
完成之后,最后一步是获取布局信息并将像素绘制到屏幕上。
作者:James Starkie 译者:前端小智 来源:dev
原文:https://dev.to/jstarmx/how-the-browser-renders-a-web-page-1ah
本文实例讲述了js HTML DOM EventListener功能与用法。分享给大家供大家参考,具体如下:
DOM EventListener
用于向指定元素添加事件句柄。
在用户点击按钮时触发监听事件:
document.getElementById(\'myBtn\').addEventListener(\'click\', displayDate); |
addEventListener() 方法添加的事件句柄不会覆盖已存在的事件句柄。
可以向一个元素添加多个事件句柄。
<script> var x = document.getElementById(\"myBtn\"); x.addEventListener(\"mouseover\", myFunction); x.addEventListener(\"click\", mySecondFunction); x.addEventListener(\"mouseout\", myThirdFunction); function myFunction() { document.getElementById(\"demo\").innerHTML += \"Moused over!<br>\" } function mySecondFunction() { document.getElementById(\"demo\").innerHTML += \"Clicked!<br>\" } function myThirdFunction() { document.getElementById(\"demo\").innerHTML += \"Moused out!<br>\" } </script> |
可以向同个元素添加多个同类型的事件句柄,如:两个 \"click\" 事件。
<script> var x = document.getElementById(\"myBtn\"); x.addEventListener(\"click\", myFunction); x.addEventListener(\"click\", someOtherFunction); function myFunction() { alert (\"Hello World!\") } function someOtherFunction() { alert (\"函数已执行!\") } </script> |
可以向任何 DOM 对象添加事件监听,不仅仅是 HTML 元素。如: window 对象。
addEventListener() 方法允许你在 HTML DOM 对象添加事件监听, HTML DOM 对象如: HTML 元素, HTML 文档, window 对象。或者其他支出的事件对象如: xmlHttpRequest 对象
<script> window.addEventListener(\"resize\", function(){ document.getElementById(\"demo\").innerHTML = Math.random(); }); </script> |
addEventListener() 方法可以更简单的控制事件(冒泡与捕获)。
当使用 addEventListener() 方法时, JavaScript 从 HTML 标记中分离开来,可读性更强, 在没有控制HTML标记时也可以添加事件监听。
可以使用 removeEventListener() 方法来移除事件的监听。
语法
element.addEventListener(event, function, useCapture); |
第一个参数是事件的类型 (如 \"click\" 或 \"mousedown\").
第二个参数是事件触发后调用的函数。
第三个参数是个布尔值用于描述事件是冒泡还是捕获。该参数是可选的。
注意:不要使用 \"on\" 前缀。 例如,使用 \"click\" ,而不是使用 \"onclick\"。
事件冒泡或事件捕获?
事件传递有两种方式:冒泡与捕获。
事件传递定义了元素事件触发的顺序。 如果你将 <p> 元素插入到 <div> 元素中,用户点击 <p> 元素, 哪个元素的 \"click\" 事件先被触发呢?
在 冒泡 中,内部元素的事件会先被触发,然后再触发外部元素,即: <p> 元素的点击事件先触发,然后会触发 <div> 元素的点击事件。
在 捕获 中,外部元素的事件会先被触发,然后才会触发内部元素的事件,即: <div> 元素的点击事件先触发 ,然后再触发 <p> 元素的点击事件。
addEventListener()方法可以指定 \"useCapture\" 参数来设置传递类型:
addEventListener(event, function, useCapture); |
默认值为 false, 即冒泡传递,当值为 true 时, 事件使用捕获传递。
<script> document.getElementById(\"myP\").addEventListener(\"click\", function() { alert(\"你点击了 P 元素!\"); }, false); document.getElementById(\"myDiv\").addEventListener(\"click\", function() { alert(\" 你点击了 DIV 元素 !\"); }, false); document.getElementById(\"myP2\").addEventListener(\"click\", function() { alert(\"你点击了 P2 元素!\"); }, true); document.getElementById(\"myDiv2\").addEventListener(\"click\", function() { alert(\"你点击了 DIV2 元素 !\"); }, true); </script> |
removeEventListener() 方法
removeEventListener()方法移除由addEventListener()方法添加的事件句柄:
<script> document.getElementById(\"myDIV\").addEventListener(\"mousemove\", myFunction); function myFunction() { document.getElementById(\"demo\").innerHTML = Math.random(); } function removeHandler() { document.getElementById(\"myDIV\").removeEventListener(\"mousemove\", myFunction); } </script> |
浏览器支持
表格中的数字表示支持该方法的第一个浏览器的版本号。
图片1
注意: IE 8 及更早 IE 版本,Opera 7.0及其更早版本不支持addEventListener()和removeEventListener()方法。但是,对于这类浏览器版本可以使用detachEvent()方法来移除事件句柄:
element.attachEvent(event, function); element.detachEvent(event, function); |
例:该实例演示了所有浏览器兼容的解决方法
<script> var x = document.getElementById(\"myBtn\"); if (x.addEventListener) { x.addEventListener(\"click\", myFunction); } else if (x.attachEvent) { x.attachEvent(\"onclick\", myFunction); } function myFunction() { alert(\"Hello World!\"); } </script> |
Demand feedback