HTML渲染流程如何详细解析?

2026-05-27 09:202阅读0评论SEO资讯
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1853个文字,预计阅读时间需要8分钟。

HTML渲染流程如何详细解析?

在探索寒冬时,我注意到前端领域存在九个关键问题。具体来说,我对前九个问题已有了解,并希望借此机会梳理自己的知识体系。由于我对HTTP协议及DNS对URL解析的理解尚不充分,以下是我对相关知识的整理。

  无意中看到寒冬关于前端的九个问题,细细想来我也只是对第一、二、九问有所了解,正好也趁着这个机会梳理一下自己的知识体系。由于本人对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> View Code

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

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>JS Bin</title> <script>var start = +new Date;</script> <link href="udacity-crp.herokuapp.com/style.css?rtt=2" rel="stylesheet"> </head> <body> test <script src="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); 外部脚本

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

HTML渲染流程如何详细解析?

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

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>JS Bin</title> <script>var start = +new Date;</script> <link href="udacity-crp.herokuapp.com/style.css?rtt=2" rel="stylesheet"> </head> <body> test <script src="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> View Code

  从瀑布图中可以看到外部脚本的加载与执行并不受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 = "udacity-crp.herokuapp.com/style.css?rtt=2"; link.rel = "stylesheet"; document.head.appendChild(link); var script = document.createElement('script'); script.src = "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> View Code

  这是最终页面结构

  通过瀑布图与页面结果可以看到动态创建的外部脚本并未受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. 轮廓

参考资料:

  • velocity.oreilly.com.cn/2010/ppts/limufromTaobao.pdf
  • lifesinger.wordpress.com/
  • hikejun.com/blog/2012/02/02/js%E5%92%8Ccss%E7%9A%84%E9%A1%BA%E5%BA%8F%E5%85%B3%E7%B3%BB/
  • www.html5rocks.com/zh/tutorials/internals/howbrowserswork/
  • www.2cto.com/kf/201406/305852.html
  • www.w3cmm.com/dom/document-stylesheets-getstylesheet.html
  • www.cnblogs.com/wenanry/archive/2010/02/25/1673368.html

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

您可以考虑给树发个小额微信红包以资鼓励

本文共计1853个文字,预计阅读时间需要8分钟。

HTML渲染流程如何详细解析?

在探索寒冬时,我注意到前端领域存在九个关键问题。具体来说,我对前九个问题已有了解,并希望借此机会梳理自己的知识体系。由于我对HTTP协议及DNS对URL解析的理解尚不充分,以下是我对相关知识的整理。

  无意中看到寒冬关于前端的九个问题,细细想来我也只是对第一、二、九问有所了解,正好也趁着这个机会梳理一下自己的知识体系。由于本人对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> View Code

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

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>JS Bin</title> <script>var start = +new Date;</script> <link href="udacity-crp.herokuapp.com/style.css?rtt=2" rel="stylesheet"> </head> <body> test <script src="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); 外部脚本

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

HTML渲染流程如何详细解析?

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

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>JS Bin</title> <script>var start = +new Date;</script> <link href="udacity-crp.herokuapp.com/style.css?rtt=2" rel="stylesheet"> </head> <body> test <script src="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> View Code

  从瀑布图中可以看到外部脚本的加载与执行并不受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 = "udacity-crp.herokuapp.com/style.css?rtt=2"; link.rel = "stylesheet"; document.head.appendChild(link); var script = document.createElement('script'); script.src = "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> View Code

  这是最终页面结构

  通过瀑布图与页面结果可以看到动态创建的外部脚本并未受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. 轮廓

参考资料:

  • velocity.oreilly.com.cn/2010/ppts/limufromTaobao.pdf
  • lifesinger.wordpress.com/
  • hikejun.com/blog/2012/02/02/js%E5%92%8Ccss%E7%9A%84%E9%A1%BA%E5%BA%8F%E5%85%B3%E7%B3%BB/
  • www.html5rocks.com/zh/tutorials/internals/howbrowserswork/
  • www.2cto.com/kf/201406/305852.html
  • www.w3cmm.com/dom/document-stylesheets-getstylesheet.html
  • www.cnblogs.com/wenanry/archive/2010/02/25/1673368.html

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

您可以考虑给树发个小额微信红包以资鼓励