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

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

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

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

相关推荐
腾讯TNTWeb前端团队3 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰7 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪7 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪7 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy8 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom8 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom8 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom8 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom8 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom9 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试