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

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

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

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

相关推荐
YBN娜6 分钟前
Vue实现登录功能
前端·javascript·vue.js
阳光开朗大男孩 = ̄ω ̄=6 分钟前
CSS——选择器、PxCook软件、盒子模型
前端·javascript·css
minDuck11 分钟前
ruoyi-vue集成tianai-captcha验证码
java·前端·vue.js
小政爱学习!31 分钟前
封装axios、环境变量、api解耦、解决跨域、全局组件注入
开发语言·前端·javascript
魏大帅。37 分钟前
Axios 的 responseType 属性详解及 Blob 与 ArrayBuffer 解析
前端·javascript·ajax
花花鱼43 分钟前
vue3 基于element-plus进行的一个可拖动改变导航与内容区域大小的简单方法
前端·javascript·elementui
k09331 小时前
sourceTree回滚版本到某次提交
开发语言·前端·javascript
EricWang13581 小时前
[OS] 项目三-2-proc.c: exit(int status)
服务器·c语言·前端
September_ning1 小时前
React.lazy() 懒加载
前端·react.js·前端框架
web行路人1 小时前
React中类组件和函数组件的理解和区别
前端·javascript·react.js·前端框架