浏览器渲染原理-构建阶段

浏览器渲染原理-解析阶段

解析阶段获取到DOM和CSSOM后,进入渲染树构建阶段

任务

将DOM和CSSOM相结合,给每一个可见DOM节点创建渲染对象,将这些渲染对象参照DOM树结构结合起来,形成"清洗后赋予样式的DOM树"(渲染树)

流程

渲染阶段主要分为5个步骤

  1. 遍历DOM树
  2. 去除不可见节点
  3. 生成渲染对象
  4. 应用CSS
  5. 生成渲染树

其中2~5是一个重复的过程,针对于每一个DOM节点

步骤详情描述

  • 遍历DOM树

一个递归的过程(深度优先搜索DFS),依次从DOM根节点读取每一个节点

  • 去除不可见节点

去除不可见节点分为两种情况 情况一:初始遍历DOM时 初始遍历DOM时,会去除一些常见不可见标签(head、link、style、script等)以及在解析阶段已经能够判断出不可见的节点(<div style="display: none;"></div>)。 情况二:应用完成CSS后 节点应用完成CSS后,挂载到渲染树之前,会判断节点是否可见,如果不可见,节点将不会被挂载。

  • 生成渲染对象

渲染对象在初次遍历DOM时创建,记录DOM的嵌套信息并注入一些DOM操作API(如:appendChild)。

  • 应用CSS

遍历CSSOM规则列表,筛选命中的样式,每一个属性根据CSS选择器优先级计算出最终样式。最终样式注入到渲染对象的styles属性。

  • 构建渲染树

将可见的渲染对象参考DOM结构,挂载到对应位置。 渲染树构建是一个逐步的过程,每生成一个渲染对象就挂载一个节点,最终构成一棵树结构。

生成规则样例

构建伪代码

js 复制代码
function buildRenderTree(domNode) {
    // 跳过不可见节点
    if (isHidden(domNode)) return null;

    // 创建渲染对象
    const renderObject = createRenderObject(domNode);

    // 递归遍历子节点并构建子渲染对象
    let child = domNode.firstChild;
    while (child) {
        const childRenderObject = buildRenderTree(child);
        if (childRenderObject) {
            renderObject.appendChild(childRenderObject);
        }
        child = child.nextSibling;
    }

    return renderObject;
}

function isHidden(domNode) {
    if (domNode.nodeType === Node.ELEMENT_NODE) {
        const style = window.getComputedStyle(domNode);
        return style.display === 'none';
    }
    return false;
}

function createRenderObject(domNode) {
    const renderObject = {
        // 渲染对象结构
        styles: window.getComputedStyle(domNode),
        children: [],
        appendChild: function(child) {
            this.children.push(child);
        }
    };
    return renderObject;
}

// 示例调用,从根节点开始构建渲染树
const renderTree = buildRenderTree(document.documentElement);
console.log(renderTree);

渲染树结构

js 复制代码
// html
<!DOCTYPE html>
<html>
<head>
    <style>
        body { font-family: Arial; }
        .container { color: blue; }
        p { font-size: 16px; }
        .hidden { display: none; }
    </style>
</head>
<body>
    <div class="container">
        <p>Hello, World!</p>
        <p class="hidden">This is hidden</p>
    </div>
</body>
</html>

Document
└── html
    ├── head
    └── body
        └── div.container
            ├── p
            │   └── "Hello, World!"
            └── p.hidden
                └── "This is hidden"

CSSOM
└── Stylesheet
    ├── Rule: body { font-family: Arial; }
    ├── Rule: .container { color: blue; }
    ├── Rule: p { font-size: 16px; }
    └── Rule: .hidden { display: none; }

RenderRoot
└── RenderBody (type: 'block', styles: { font-family: 'Arial' })
    └── RenderBlock (type: 'block', styles: { color: 'blue' })
        └── RenderParagraph (type: 'block', styles: { font-size: '16px' })
            └── RenderText (type: 'inline', styles: {}, text: 'Hello, World!')

构建阶段得到渲染树后,进入布局阶段

相关推荐
开发者小天23 分钟前
为什么 /deep/ 现在不推荐使用?
前端·javascript·node.js
如白驹过隙1 小时前
cloudflare缓存配置
前端·缓存
excel1 小时前
JavaScript 异步编程全解析:Promise、Async/Await 与进阶技巧
前端
Jerry说前后端1 小时前
Android 组件封装实践:从解耦到架构演进
android·前端·架构
步行cgn2 小时前
在 HTML 表单中,name 和 value 属性在 GET 和 POST 请求中的对应关系如下:
前端·hive·html
hrrrrb2 小时前
【Java Web 快速入门】十一、Spring Boot 原理
java·前端·spring boot
找不到工作的菜鸟2 小时前
Three.js三大组件:场景(Scene)、相机(Camera)、渲染器(Renderer)
前端·javascript·html
定栓2 小时前
vue3入门-v-model、ref和reactive讲解
前端·javascript·vue.js
专注API从业者2 小时前
基于 Flink 的淘宝实时数据管道设计:商品详情流式处理与异构存储
大数据·前端·数据库·数据挖掘·flink
龙在天2 小时前
H5开发,开发照相机,以及组件封装
前端