Appearance
渲染三棵树原理
ArkUI 的渲染引擎核心:逻辑树 → 渲染树 → 节点树(LayerTree/RSNode)。
1. 渲染三棵树概览
用户操作(点击/状态变化)
↓
【第一棵:逻辑树 ViewTree】
ArkUI 框架层
- 组件描述
- 状态管理
- 数据绑定
↓ Diff
【第二棵:渲染树 RenderTree】
ArkUI RenderService
- 布局计算(Layout/Measure)
- 样式计算
- 节点树生成
↓
【第三棵:节点树 LayerTree/RSNode】
Render Service / GPU
- 绘制(Draw)
- 合成(Composition)
- 显存管理
↓
屏幕显示2. 第一棵:逻辑树(ViewTree)
2.1 职责
逻辑树是 ArkUI 的UI 描述层,由框架创建和维护:
- 组件树结构的创建和管理
- 状态管理(@State/@Prop/@Link 等)
- Diff 算法对比新旧 UI 描述
- 生成渲染树变更指令
2.2 生命周期
组件创建 → 逻辑树节点创建
↓
状态变化 → Diff 对比 → 变更标记
↓
渲染树更新 → 变更下发到渲染树
↓
组件销毁 → 逻辑树节点销毁2.3 Diff 算法
typescript
// 状态变化后,ArkUI 执行 Diff 对比
const oldTree = build() // 旧的 UI 描述
const newTree = build() // 新的 UI 描述
// Diff 对比(三层)
for (let i = 0; i < newTree.length; i++) {
if (newTree[i] === oldTree[i]) {
// 相同 → 跳过
} else if (newTree[i].type === oldTree[i].type) {
// 类型相同 → 对比属性,更新变化的属性
} else {
// 类型不同 → 删除旧节点,创建新节点
}
}3. 第二棵:渲染树(RenderTree)
3.1 职责
渲染树是 ArkUI 的布局计算层:
- 执行 Measure(测量)→ Layout(布局)→ Draw(绘制)
- 坐标系统计算
- 层合并(Layer Merging)优化
- 渲染指令生成
3.2 渲染流程
1. Measure(测量)
- 计算每个节点需要的尺寸
- 从叶子节点向根节点传递(Bottom-up)
2. Layout(布局)
- 根据测量结果确定每个节点的位置
- 从根节点向叶子节点传递(Top-down)
3. Draw(绘制)
- 生成绘制指令
- 传递给渲染服务(RenderService)3.3 层合并(Layer Merging)优化
未合并:
┌─────┐ ┌─────┐ ┌─────┐
│ Text │ │Text │ │Text │
└─────┘ └─────┘ └─────┘
(3 个渲染层)
层合并后:
┌─────────────────┐
│ Text Text Text │
└─────────────────┘
(1 个渲染层)
效果:减少 GPU 层数,提升渲染性能4. 第三棵:节点树(LayerTree/RSNode)
4.1 职责
节点树是渲染服务层(RenderService),直接操作 GPU:
- FrameNode → RenderNode 的映射
- GPU 显存管理
- 绘制指令执行
- 图层合成
4.2 FrameNode vs RenderNode
| 概念 | 层级 | 说明 |
|---|---|---|
| FrameNode | ArkUI 层 | 开发者可见的 UI 组件节点 |
| RenderNode | RenderService 层 | 底层渲染对象,不可见 |
ArkUI 层 RenderService 层
FrameNode (UI节点) → RenderNode (渲染对象)
FrameNode (UI节点) → RenderNode (渲染对象)
↓ ↓
Diff 对比 (ArkUI) 变更下发 (RenderService)
↓ ↓
Measure/Layout/Draw → GPU 绘制 → 合成 → 屏幕5. 渲染优化的核心思路
5.1 减少不必要的重建
❌ 触发全量重建:
├─ 修改了根节点的 @State
├─ build() 中嵌套太深(Diff 开销大)
└─ 频繁创建/销毁组件节点
✅ 局部刷新:
├─ 修改局部 @State(只重建该节点及其子树)
├─ 使用 LazyForEach(按需渲染)
└─ 组件提取复用(减少 Diff 范围)5.2 减少层数
✅ 减少层数的方法:
├─ 合并相邻相同样式的文本(层合并)
├─ 使用固定宽高(避免 Measure 重复计算)
├─ 减少嵌套深度(降低布局计算量)
└─ 使用图片下采样(减少显存占用)5.3 渲染三棵树性能影响
| 优化点 | 影响哪棵树 | 效果 | |---|-|-|--| | 固定宽高 | 第二棵(RenderTree) | 避免重复 Measure | | 减少嵌套 | 第一棵 + 第二棵 | 减少 Diff + 布局开销 | | LazyForEach | 第二棵 + 第三棵 | 按需创建渲染层 | | 图片下采样 | 第三棵(LayerTree) | 减少显存占用 | | 层合并 | 第三棵(LayerTree) | 减少 GPU 层数 |
6. 面试高频考点
Q1: 渲染三棵树分别是什么?
回答:
- 逻辑树(ViewTree):UI 描述层,包含组件树和状态管理,执行 Diff 对比
- 渲染树(RenderTree):布局计算层,执行 Measure → Layout → Draw
- 节点树(LayerTree/RSNode):渲染服务层,映射到 GPU,执行绘制和合成
Q2: FrameNode 和 RenderNode 的区别?
回答:FrameNode 是 ArkUI 层开发者可见的 UI 节点,RenderNode 是 RenderService 层底层渲染对象。FrameNode 通过 Diff 对比生成变更指令,传递给 RenderNode 执行。
Q3: 层合并(Layer Merging)的作用?
回答:将相邻相同样式的组件合并为同一个渲染层,减少 GPU 层数,提升渲染性能。
🐱 小猫提示:渲染三棵树是鸿蒙面试中最硬核的原理题。记住:ViewTree(Diff)→ RenderTree(Measure/Layout/Draw)→ LayerTree(GPU绘制/合成)。面试中能画出来就稳了。