一起来学习浏览器渲染原理吧

首先来看一张图片

一、浏览器的渲染过程

从解析 HTML 和 CSS 到最终页面呈现。以下是浏览器的渲染过程的主要步骤:

  • 浏览器开始解析 HTML,转换收到的数据为 DOM 树。
    1. 浏览器每次发现外部资源就初始化请求,无论是样式、脚本或者嵌入的图片引用。
    1. 有时请求会阻塞,这意味着解析剩下的 HTML 会被终止直到重要的资源被处理。
    1. 浏览器接着解析 HTML,发请求和构造 DOM 直到文件结尾(HTML 解析->DOM)
    1. 这时开始构造 CSS 对象模型, 树表示了页面的样式信息,包括每个元素的样式规则和计算出的样式值(CSS 解析->CSSOM 树)
    1. 等到 DOM 和 CSSOM 完成之后,浏览器构造渲染树(Render Tree)
    1. 计算(布局Layout)所有可见内容的样式。
    1. 一旦渲染树完成布局开始,定义所有渲染树元素的位置和大小(Paint)
    1. 完成之后,页面被渲染完成,或者说是绘制到屏幕上。

这些步骤是逐渐进行的,且是异步执行的。在渲染过程中,浏览器会通过优化手段提高性能,例如将一些操作合并为一次执行、使用异步加载脚本等。渲染过程中的每个步骤都对页面性能有影响,因此优化渲染过程是提高网页性能的关键。

补充

预加载扫描器

浏览器构建 DOM 树时,这个过程占用了主线程。当这种情况发生时,预加载扫描仪将解析可用的内容请求高优先级资源,如 CSS、JavaScript 和 web 字体。多亏了预加载扫描器,我们不必等到解析器找到对外部资源的引用来请求它。它将在后台检索资源,以便在主 HTML 解析器到达请求的资源时,它们可能已经在运行,或者已经被下载。 预加载扫描仪提供的优化减少了阻塞。

二 、html解析过程中遇到js代码会怎么样

当 HTML 解析过程中遇到包含 JavaScript 代码的 <script> 标签时,默认是同步执行,就是说浏览器只能干完一件事再干另一件,不能同时做html解析和处理js这两件事,这个过程可能会对渲染和页面加载产生影响,具体表现如下:

多种表现形式:

同步执行

  1. HTML 解析暂停 并且 阻塞渲染:

    • 遇到 <script> 标签时,会暂停 HTML 解析过程,以执行 JavaScript 代码。
    • 默认情况下,浏览器会阻塞渲染,直到 JavaScript 代码执行完成。这会导致页面渲染被延迟,用户可能会感觉到页面加载速度较慢。
  2. JavaScript 代码执行:

    • 遇到<script> 标签时,会先加载(把资源下载下来),在解析成可执行机器码,然后执行;具体解释看这(todo);
    • 执行的过程包括变量声明、函数定义、事件绑定等。

异步执行(async)

  1. 异步执行:
  • 如果 <script> 标签设置了 async 属性,表示 JavaScript 代码可以异步加载、解析、执行,不会阻塞 HTML 解析和渲染过程。一旦 JavaScript 文件下载完成,会立即执行,无论DOM是否构建完成;
  • 多个带有 async 属性的脚本,在 HTML 中的并行执行, 顺序不确定;
  1. 影响 DOM 和 CSSOM 构建:
  • JavaScript 代码可能会修改 DOM 结构和样式,因此它的执行可能会影响到 DOM 树和 CSSOM 树的构建过程。

延迟执行(defer)

  1. 延迟执行(defer):
  • 如果 <script> 标签设置了 defer 属性,表示 JavaScript 代码将延迟到文档解析完成后执行,但会在 DOMContentLoaded 事件之前执行。DOMContentLoaded会在DOM 树构建完成之后触发;
  • 多个带有 defer 属性的脚本会按照它们在 HTML 中的顺序依次执行。

async 和 defer 同时存在

  1. async优先级高:
  • async优先级高,按照异步执行顺序;

    总体来说,默认情况下,JS代码阻塞 HTML 解析 阻塞渲染 影响页面加载性能;使用 asyncdefer 属性的脚本不会阻塞 HTML 解析和渲染过程,有助于提高页面的加载性能。选择使用哪个属性取决于脚本的执行时机需求:

  • 使用 async 当不关心脚本的执行顺序,且脚本独立于其他脚本。

  • 使用 defer 当希望保持脚本按照它们在 HTML 中的顺序执行,但又不想阻塞 HTML 解析。

三、 script 标签使用 async 或者 defer,js脚本就会下载和执行,如果代码中有操作dom的代码,执行会报错, 如何避免

  • 如果 异步执行async 脚本尝试访问尚未解析的 DOM 元素,可能会导致 DOM 元素不存在的情况,从而引发错误。
  • 如果 延迟执行defer 脚本尝试访问尚未解析的 DOM 元素,可能会导致问题,但通常情况下,由于 defer 会等待 HTML 解析完成,大部分 DOM 操作是安全的。

会导致的其他问题:

  • 如果脚本尝试在异步加载期间绑定事件到尚未存在的元素上,可能会导致事件无法正确绑定;
为了避免这些问题,可以采取以下策略:

1. 推迟 DOM 操作:

  • 在异步加载的脚本中,确保 DOM 操作发生在 DOM 元素完全构建后。可以使用 DOMContentLoaded 事件来确保在 DOM 就绪后再执行相应的操作。
js 复制代码
    document.addEventListener('DOMContentLoaded', function() { 
       // 在此处进行 DOM 操作 
    });

2.动态加载脚本:

  • 可以通过 JavaScript 动态创建 <script> 标签,并设置 asyncdefer 属性,以更好地控制脚本的加载和执行时机。
js 复制代码
    var script = document.createElement('script'); 
    script.src = 'example.js'; 
    script.async = true; // 或者 script.defer = true;   
    document.head.appendChild(script);

3.脚本位置:

  • 推荐将脚本放在页面底部(在 </body> 标签之前), 以确保在 HTML 解析完成前加载。

四、html解析过程中遇到css代码会怎么样

  • 包括:处理内联样式(inline styles)外部样式表(link 外部样式)、以及通过 @import 导入的样式表的流程如下
  1. 内联样式(Inline Styles):

    • 内联样式是直接嵌入在 HTML 元素中的样式,通过 style 属性定义。
    • 浏览器在解析 HTML 的过程中,会识别每个包含内联样式的元素,并将这些样式应用到相应的元素上。
    html 复制代码
        <div style="color: red;">This is a red text.</div>
  2. 外部样式表(External Stylesheet):

    • 外部样式表是通过 <link> 标签引入的独立的 CSS 文件。
    • 遇到 <link> 标签时会异步下载外部样式表,但不会阻塞 HTML 解析。一旦外部样式表下载完成,浏览器会应用样式表中的规则。
    html 复制代码
        <link rel="stylesheet" type="text/css" href="styles.css">
  3. @import 导入样式表:

    过程

    • @import 是 CSS 提供的一种方式,可以在样式表中导入其他样式表。
    • 浏览器在解析样式表时,会处理 @import 语句。
    • 遇到 <style> 标签中的 @import 声明时,它会识别并记录下这些导入语句。
    • 异步加载外部样式表,开始加载,等待样式表加载完成, 但不会阻塞 HTML 解析和渲染。
    • 并行加载样式表,浏览器会尽可能并行加载多个外部样式表。
    • 样式表加载完成后应用样式,一旦样式表加载完成,浏览器会应用其中的样式规则,影响文档的渲染
    css 复制代码
        /* styles.css */
        @import url('imported-styles.css');
        body {
          background-color: #f0f0f0;
        }
    css 复制代码
        /* imported-styles.css */
        h1 {
          color: blue;
        }

    需要注意的是,虽然 @import 可以用于动态加载外部样式表,但它也可能导致一些性能问题,因为它引入了额外的网络请求。推荐的做法是在页面的 <head> 部分使用 <link> 标签来直接引入样式表,以利用浏览器的并行下载机制,提高页面加载性能。

    可能导致的问题

    1.影响 CSSOM 阶段**

    • 在 CSSOM 的构建阶段,浏览器需要等待通过 @import 导入的样式表加载完成后,才能获取到其中的样式规则。这可能会在构建 CSSOM 的过程中引入一些延迟。

    2.样式规则的应用:

    • 在 CSSOM 构建完成后,浏览器会将样式规则应用于 DOM 树,影响文档的渲染。样式表加载的异步性可能会导致一部分文档在构建 CSSOM 之前已经渲染,但在加载样式表后需要重新渲染。

五、了解CSSDOM

在 JavaScript 中,要访问和操作 CSSOM(CSS Object Model)对象,你可以使用一些 DOM 接口和方法。 例如:

  • 通过 document.styleSheets 返回一个样式表列表,你可以通过索引访问其中的样式表。每个样式表都有一个 cssRules 属性,它包含样式规则列表。
css 复制代码
    // 获取第一个样式表 
    var styleSheet = document.styleSheets[0]; 
    // 获取样式规则列表 
    var rules = styleSheet.cssRules || styleSheet.rules;
    // 遍历样式规则 
    for (var i = 0; i < rules.length; i++) { 
        console.log(rules[i].cssText); 
    }
  • 使用 document.querySelector 获取元素的样式:
css 复制代码
    // 获取第一个 p 元素的内联样式 
    var paragraphStyle = document.querySelector('p').style; 
    // 修改内联样式 
    paragraphStyle.color = 'blue';
  • 通过 getComputedStyle 获取计算后的样式: 可以获取计算后的样式,即应用在元素上的最终样式
css 复制代码
    // 获取第一个 p 元素的计算后样式 
    var computedStyle = getComputedStyle(document.querySelector('p')); 
    // 获取计算后的颜色值 var color = computedStyle.color;

六、 浏览器渲染优化,提升加载速度

  1. 针对JS:
  • 也就是前面的JS阻塞 html解析和渲染的解决方案:
  • 脚本位置: script放在最后,之前;
  • 异步加载: async;
  • 延迟执行: defer;
  • 动态加载脚本;
  1. 针对CSS:
  • 使用外部样式link的方式;
  1. 针对DOM构建:
  • HTML文件的代码层级尽量不要太深
  • 使用语义化的标签,来避免不标准语义化的特殊处理
  • 减少CSS代码的层级
  1. 减少回流与重绘

总的来说就是,·优化关键渲染路径CRP(构建dom -》 构建cssDOM -》 构建渲染树 -》 布局 -》 绘制)。提升页面加载速度需要通过被加载资源的优先级控制它们加载的顺序减小这些资源的体积。 性能提升包含

  • 通过异步、延迟加载或者消除非关键资源来减少关键资源的请求数量,
  • 优化必须的请求数量和每个请求的文件体积
  • 通过区分关键资源的优先级来优化被加载关键资源的顺序,来缩短关键路径长度。
相关推荐
Asort3 分钟前
JavaScript 从零开始(六):控制流语句详解——让代码拥有决策与重复能力
前端·javascript
无双_Joney22 分钟前
[更新迭代 - 1] Nestjs 在24年底更新了啥?(功能篇)
前端·后端·nestjs
在云端易逍遥23 分钟前
前端必学的 CSS Grid 布局体系
前端·css
ccnocare25 分钟前
选择文件夹路径
前端
艾小码25 分钟前
还在被超长列表卡到崩溃?3招搞定虚拟滚动,性能直接起飞!
前端·javascript·react.js
闰五月26 分钟前
JavaScript作用域与作用域链详解
前端·面试
泉城老铁30 分钟前
idea 优化卡顿
前端·后端·敏捷开发
前端康师傅30 分钟前
JavaScript 作用域常见问题及解决方案
前端·javascript
司宸31 分钟前
Prompt结构化输出:从入门到精通的系统指南
前端