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

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

解析阶段获取到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!')

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

相关推荐
Momo__2 小时前
Vite 8.1 打包开发模式——10000 组件冷启动 15 倍,"No Bundle Dev" 七年后被自己终结
前端·vue.js·vite
YHL2 小时前
🤖 Agent 智能体开发实战 · 第一课:Tool Use —— 让大模型自动干活
前端·javascript·人工智能
Monki2 小时前
AI 规范式 UI 设计,一键批量出页,高效落地不翻车
前端
山河木马2 小时前
渲染管线-计算得到gl_FragColor(片元着色器)之后续GPU流程
前端·webgl·计算机图形学
不想说话的麋鹿2 小时前
「项目实战」我用 Codex + GPT-5.5 + Trellis "氛围编程",独立做了一个情侣厨房小程序
前端·agent·全栈
接着奏乐接着舞。4 小时前
【2026年7月最新】69道RAG面试题
前端·人工智能·后端·aigc·embedding·rag
以和为贵4 小时前
前端也能搞懂 Agent:从 Function Calling 到自主编排
前端·人工智能·架构
风止何安啊4 小时前
🚦 前端并发请求 “交通管制”:别让你的接口堵成早高峰
前端·javascript·面试
J船长4 小时前
扫盲烂笔头:A 记录、CNAME 的作用, Nginx的用途,为啥叫反向代理
前端
妙码生花4 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(二十六):icon 组件封装
前端·vue.js·typescript