解析阶段获取到DOM和CSSOM后,进入渲染树构建阶段
任务
将DOM和CSSOM相结合,给每一个可见DOM节点创建渲染对象,将这些渲染对象参照DOM树结构结合起来,形成"清洗后赋予样式的DOM树"(渲染树)
流程
渲染阶段主要分为5个步骤
- 遍历DOM树
- 去除不可见节点
- 生成渲染对象
- 应用CSS
- 生成渲染树
其中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!')
构建阶段得到渲染树后,进入布局阶段