HTML渲染过程详解

 无意中看到寒冬关于前端的九个问题,细细想来我也只是对第一、二、九问有所了解,正好也趁着这个机会梳理一下自己的知识体系。由于本人对http协议以及dns对url的解析问题并不了解,所以这里之探讨url请求加载到浏览器端时,浏览器对html的解析到呈现过程,后来经过几位道友分享,整理了一下url解析的过程,如下:

  1. 用户输入url地址,浏览器根据域名寻找IP地址
  2. 浏览器向服务器发送http请求,如果服务器段返回以301之类的重定向,浏览器根据相应头中的location再次发送请求
  3. 服务器端接受请求,处理请求生成html代码,返回给浏览器,这时的html页面代码可能是经过压缩的
  4. 浏览器接收服务器响应结果,如果有压缩则首先进行解压处理,紧接着就是页面解析渲染

  解析渲染该过程主要分为以下步骤:

  1. 解析HTML
  2. 构建DOM树
  3. DOM树与CSS样式进行附着构造呈现树
  4. 布局
  5. 绘制

解析与构建DOM树

  前两步我们放在一起讨论,浏览器的实际工作也是将他们放在一起进行的。对于HTML浏览器有专门的html解析器来解析HTML,并在解析的过程中构建DOM树。在这里我们讨论两种DOM元素的解析,即样式(link、style)与脚本文件(script)。由于浏览器采用自上而下的方式解析,在遇到这两种元素时都会阻塞浏览器的解析,直到外部资源加载并解析或执行完毕后才会继续向下解析html。对于样式与脚本的先后顺序同样也会影响到浏览器的解析过程,究其原因主要在于:script脚本执行过程中可能会修改html界面(如document.write函数);DOM节点的CSS样式会影响js的执行结果。在我的测试中得到以下四条结论:

  1)外部样式会阻塞后续脚本执行,直到外部样式加载并解析完毕。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
  <script>var start = +new Date;</script>
  <link href="http://udacity-crp.herokuapp.com/style.css?rtt=2" rel="stylesheet">

</head>

<body>
  <span id="result"></span>
  <script>
    var end = +new Date;
    document.getElementById('result').innerHTML = (end-start);
  </script>
</body>
</html>

  2)外部样式不会阻塞后续外部脚本的加载,但会阻塞外部脚本的执行。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
  <script>var start = +new Date;</script>
  <link href="http://udacity-crp.herokuapp.com/style.css?rtt=2" rel="stylesheet">

</head>

<body>
  test
  <script src="http://udacity-crp.herokuapp.com/time.js?rtt=1&a"></script>
  <div id="result"></div>
  <script>var end = +new Date;document.getElementById("result").innerHTML = end-start;</script>

</body>
</html>

主页代码

var loadTime = document.createElement('div');
loadTime.innerText = document.currentScript.src + ' executed @ ' + window.performance.now();
loadTime.style.color = 'blue';
document.body.appendChild(loadTime);

  从瀑布图中我们可以看到,外部脚本与外部样式是并行加载,但直到外部样式加载完毕,外部脚本才开始执行

  3)如果后续外部脚本含有async属性(IE下为defer),则外部样式不会阻塞该脚本的加载与执行

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
  <script>var start = +new Date;</script>
  <link href="http://udacity-crp.herokuapp.com/style.css?rtt=2" rel="stylesheet">

</head>

<body>
  test
  <script src="http://udacity-crp.herokuapp.com/time.js?rtt=1&a" async></script>
  <div id="result"></div>
  <script>var end = +new Date;document.getElementById("result").innerHTML = end-start;</script>

</body>
</html>

  从瀑布图中可以看到外部脚本的加载与执行并不受link的阻塞

  4)对于动态创建的link标签不会阻塞其后动态创建的script的加载与执行,不管script标签是否具有async属性,但对于其他非动态创建的script,以上三条结论仍适用

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
  <script>var start = +new Date;</script>

</head>

<body>
  test
  <script>
    var link = document.createElement('link');
    link.href = "http://udacity-crp.herokuapp.com/style.css?rtt=2";
    link.rel = "stylesheet";
    document.head.appendChild(link);
    var script = document.createElement('script');
    script.src = "http://udacity-crp.herokuapp.com/time.js?rtt=1&a";
    document.head.appendChild(script);
  </script>
  <div id="result"></div>
  <script>var end = +new Date;document.getElementById("result").innerHTML = end-start;</script>

</body>
</html>

  这是最终页面结构

  通过瀑布图与页面结果可以看到动态创建的外部脚本并未受link的阻塞。

  link或style标签都会被解析成DOM节点。浏览器对于样式表还会生成CSSStyleSheet对象(C++代码),他集成子CSSStyle,指标是样式表对象而不管该对象来自于Style还是link。该对象主要包含以下几个重要属性和方法

  • CSSRules 即css样式代码
  • type 表示样式表类型的字符串。对CSS样式表而言,这个字符串是“type/css”。
  • href 通过link生成的为样式链接,否则为undefined
  • insertRule(rule,index):向cssRules集合中指定的位置插入rule字符串。IE不支持这个方法,但支持一个类似的addRule()方法。
  • deleteRule(index):删除cssRules集合中指定的位置的规则。IE不支持这个方法,但支持一个类似的removeRule()方法。

  文档中对于所有的样式表集合可以通过document.styleSheets来访问。同时对于style或link DOM元素可以通过element.sheet来访问CSSStyleSheet对象,IE中则通过element.styleSheet来访问。

  html解析完毕,DOM树创建完成后DOMContentLoaded事件即触发,这时候可以用过script来操作DOM节点。

 

构建呈现树  

  HTML解析完毕后,开始构建呈现树RenderTree,这一步的主要工作在于将css样式应用到DOM节点上,WebKit内核将这一过程称为附着,其他浏览器有不同的概念。对前端工程师而言这个过程会涉及到CSS层叠问题。

  首先将根据样式重要性排序,由低到高依次为:

    1. 浏览器声明
    2. 用户普通声明
    3. 作者普通声明
    4. 作者重要声明
    5. 用户重要声明

  对于同一重要级别,则是根据CSS选择符的特指度来判定优先级;一条样式声明的特指度由以下四个部分决定:S-I-C-E

    1. 声明来自内联的style属性则 S+1;
    2. 声明中含有id属性则 I+1;
    3. 声明中含有类、伪类、属性选择器则 C+1;
    4. 生命中含有元素、伪元素选择器则 E+1;

  特指度的比较类似于两个字符串之间比较大小。

  呈现树的每一个节点即为与其相对应的DOM节点的CSS框,框的类型与DOM节点的display属性有关,block元素生成block框,inline元素生成inline框。每一个呈现树节点都有与之相对应的DOM节点,但DOM节点不一定有与之相对应的呈现树节点,比如display属性为none的DOM节点,而且呈现树节点在呈现树中的位置与他们在DOM树中的位置不一定相同,比如float与绝对定位元素。

  下图为呈现树与其相对应的DOM树节点

布局

  呈现树构造完成后浏览器便进行布局处理,及计算每个呈现树节点的大小和位置信息。有道友可能要问,前面已将样式附着到DOM节点上,不是已经有了样式信息为何还要计算大小。这里可以这样理解,以上包含大小的样式信息只是存在内存里,并没有实际使用,浏览器要根据窗口的实际大小来处理呈现树节点的实际显示大小和位置,比如对于margin为auto的处理。

  布局是一个递归过程,从跟呈现节点开始,递归遍历子节点,计算集合几何信息。具体过程还是比较复杂偶也不甚了解,道友们还是查阅其他资料吧。

 

绘制

   布局完成后,便是将呈现树绘制出来显示在屏幕上。对于每一个呈现树节点来说,主要绘制顺序如下:

  1. 背景颜色
  2. 背景图片
  3. 边框
  4. 子呈现树节点
  5. 轮廓

 

 参考资料:

文章有错误之处欢迎各位道友不吝指正

时间: 2024-09-22 08:15:04

HTML渲染过程详解的相关文章

Win 2000系统引导过程详解(2)

过程|详解 表一则列出了在系统引导的不同阶段所引用的文件. 表一 在这里,我们暂不讨论Windows 2000操作系统的内部工作机制,以下通过图文结合的方式,简要给大家说明在这五个阶段,操作系统都在干些什么,那些程序发挥了作用. 第一步,预引导阶段,过程如图二所示,首先包括了系统加电自检,这个过程完成硬件设备的枚举和配置.其次计算机BIOS确定引导设备的位置,加载并运行Master Boot Record(MBR)主引导区内容(如lilo等).然后扫描分区表,定位活动分区,并将活动分区上的引导扇

Win 2000系统引导过程详解(6)

过程|详解 Boot.ini文件包括[boot loader].[operating systems]两个部分,[boot loader]包括等待时间和默认引导系统两个参数,timeout后边的数字表示等待的秒数,default后边的是默认引导系统的位置.[operating systems]则包括安装的所有系统的ARC(Advanced RISC Computing)引导路径(等于号之前的部分)和他们在菜单上显示出的名字(引号之间的文本),以及启动参数(斜杠后边的部分).下边的表三将详细向您解

.NET配置文件解析过程详解

过程|详解 在我看来,WEB project的开发与WINFORM的开发最大的区别在于web的运行是在Framework上更高一层框架上运行,即ASP.NET框架,程序员在web下的开发可以说是黑盒开发,不是让你去定义程序入口和执行顺序,而是asp.net来调用你的各个方法,程序员做的一切都是一种受控的舞蹈.就像我们调用nunit之类的工具来测试一个dll一样,nunit是容器,是框架,执行哪个方法是由nunt来决定的.因此,也就有了页面执行周期各状态等令刚入门的程序员困惑不已的事,其实,究其根

AIX4.3.3上安装ORACLE9.2过程详解

oracle|过程|详解 (非原创,出自哪里我也不太记得了,共享给大家吧!单纯的安装ORACLE其实非常简单,只要按照下面的步骤,一般都不会有什么问题) AIX 4.3.3 上面安装 oracle 9.2   1. 检查物理内存:(RAM >= 512 MB)  # lsattr -El sys0 -a realmem  realmem 1048576 Amount of usable physical memory in Kbytes False  #  我们的是1G,呵呵,够了  2. 检查

Android的init过程详解(一)init的初始化

本文使用的软件版本 Android:4.2.2 Linux内核:3.1.10 本文及后续几篇文章将对Android的初始化(init)过程进行详细地.剥丝抽茧式地分析,并且在其中穿插了大量的知识,希望对读者了解Android的启动过程又所帮助.本章主要介绍了与硬件相关初始化文件名的确定以及属性服务的原理和实现. Android本质上就是一个基于Linux内核的操作系统.与Ubuntu Linux.Fedora Linux类似.只是Android在应用层专门为移动设备添加了一些特有的支持.既然An

新浪微博OAuth认证和储存的主要过程详解

 本文给大家介绍的是参考Twitter的认证过程实现的新浪微博OAuth认证和储存的主要过程详解     网上很多关于OAuth的文章,但是包括sina本身都都没有详细的的介绍,包括验证过程和验证后数据的储存,所以参考了Twitter的认证过程写下一些详细的注释代码. 在我们开始前,我们先建立一张数据库来保存用户信息,下面是一个基本的 Mysql 的例子: ? 1 2 3 4 5 6 7 8 9 CREATE TABLE `oauth_users` ( `id` INT(10) UNSIGNED

Android的init过程详解(一)(转)

  本文使用的软件版本 Android:4.2.2 Linux内核:3.1.10      本文及后续几篇文章将对Android的初始化(init)过程进行详细地.剥丝抽茧式地分析,并且在其中穿插了大量的知识,希望对读者了解Android的启动过程又所帮助.本章主要介绍了与硬件相关初始化文件名的确定以及属性服务的原理和实现.      Android本质上就是一个基于Linux内核的操作系统.与Ubuntu Linux.Fedora Linux类似.只是Android在应用层专门为移动设备添加了

IIS8 使用FastCGI配置PHP环境支持 过程详解

原文:IIS8 使用FastCGI配置PHP环境支持 过程详解   平时帮朋友们配置过一些PHP环境的服务器,但是一直使用的都是Apache HTTP+PHP,今天呢,我吧IIS+PHP配置方式给大家发一下下~呵呵.   在这里,我使用的是FastCGI模块映射的方式配置的,当然还有ISAPI处理程序映射,不过ISAPI的方式在PHP5.5之后就没有了,FastCGI是推荐的方式,效率相对比较高也稳定. 系统我用的是自己的笔记本,Windows 8.1,IIS是8的,当然Windows Serv

《嵌入式 Linux应用程序开发标准教程(第2版)》——2.2 Linux启动过程详解

2.2 Linux启动过程详解 嵌入式 Linux应用程序开发标准教程(第2版) 在了解了Linux的常见命令之后,下面详细讲解Linux的启动过程.Linux的启动过程包含了Linux工作原理的精髓,而且在嵌入式开发过程中非常需要这方面的知识. 2.2.1 概述 用户开机启动Linux过程如下: (1)当用户打开PC(intel CPU)的电源时,CPU将自动进入实模式,并从地址0xFFFF0000开始自动执行程序代码,这个地址通常是ROM-BIOS中的地址.这时BIOS进行开机自检,并按BI