ReactLynx 渲染流水线

本文以 hello-world 项目为例,详细讲解 ReactLynx 渲染流程:

  • 展示 ReactLynx 从首帧渲染到组件更新的完整执行过程
  • 理解每个关键阶段在 Trace 视图中的事件分布和时序关系
  • 掌握如何利用 Trace 工具精准定位渲染过程中的性能瓶颈
Trace Render Pipeline

首帧渲染流程

首帧渲染包括下载 Bundle、加载 Bundle、绘制等阶段。

下载 Bundle

页面启动后首先会下载 Bundle

Trace Render Pipeline

加载 Bundle

Bundle 下载完成后进入加载阶段。

Trace Render Pipeline

解析 Bundle

加载 Bundle 阶段首先解析了 Bundle 中的 CSS 样式表,脚本,页面配置等内容。

Trace Render Pipeline

执行 MTS

Bundle 解析完成后,Engine 线程虚拟机会执行 MTS

Trace Render Pipeline

执行 BTS

与此同时,后台线程虚拟机会加载,解析和执行 BTS

Execute Background Script

在 BTS 执行过程中,可以看到后台线程组件的创建过程。

Background Thread Create Component
TIP

后台线程不会阻塞 Engine 线程 Element 树的构建,Resolve,排版等流程。

构建 Element 树

构建 Element 树阶段调用 Element PAPI 将页面结构转换为 Element 树。

hello-world 项目为例,首帧创建了 1 个 page 节点,6 个 view 节点,2 个 image 节点,5 个 text 节点。

<page>
  <view className="Background" />
  <view className="App">
    <view className="Banner">
      <view className="Logo" bindtap={onTap}>
        <image src={lynxLogo} className="Logo--lynx" />
      </view>
      <text className="Title">React</text>
      <text className="Subtitle">on Lynx</text>
  </view>
  <view className="Content">
    <image src={arrow} className="Arrow" />
    <text className="Description">Tap the logo and have fun!</text>
    <text className="Hint">
      Edit<text style={{ fontStyle: "italic" }}>{" src/App.tsx "}</text>
      to see updates!
    </text>
  </view>
  <view style={{ flex: 1 }}></view>
  </view>
</page>

在 Trace 中可以看到创建 Element,设置 Element 的 Event,Class 等属性的完整过程。

Trace Render Pipeline

Resolve

Resolve 阶段根据 Element 的 class,内链样式等属性解析 Element 的 style,并创建排版节点树
如下图所示,Resolve 阶段完整执行流程和解析 <view className="Background" /> 对应 style 的流程。

Trace Render Pipeline

排版

排版阶段完成排版节点的测量和排版。如下图所示,<text>Tap the logo and have fun!</text> 节点的排版过程。

Trace Render Pipeline

Flush

完成布局后将创建平台层 UI 节点,并设置各种属性和布局信息。

Create Platform UI
TIP

只包含排版属性的 Element 节点不会创建平台层 UI。
嵌套 text 会和父节点 text 合并,创建一个平台层 UI。

hello-world 项目首帧以下节点会创建平台层 UI:

<page>
  <view className="Background" />
  <view className="Banner">
    <view className="Logo" bindtap="{onTap}">
      <image src="{lynxLogo}" className="Logo--lynx" />}
    </view>
  </view>
  <text className="Title">React</text>
  <text className="Subtitle">on Lynx</text>
  <image src="{arrow}" className="Arrow" />
  <text className="Description">Tap the logo and have fun!</text>
  <text className="Hint"> Edit " src/App.tsx " to see updates!</text>
</page>

绘制

在系统的渲染回调到来时,完成平台层 UI 的首帧绘制。

Draw Platform UI

更新渲染流程

hello-world 项目点击 Logo 触发更新为例,介绍组件更新渲染流程。

Draw Platform UI

触发点击事件

点击 Logo 后,LynxView 会处理点击事件并发送点击事件到后台线程,触发对应节点的点击事件回调

为了在 Trace 中更清晰的看到点击事件回调的执行时机,可以使用 Trace API 添加自定义 Trace 事件。

const onTap = useCallback(() => {
  'background only';
  lynx.performance.profileStart('onTap');
  setAlterLogo(!alterLogo);
  lynx.performance.profileEnd();
}, [alterLogo]);
Touch Event Process

App 组件在点击事件回调中更新了 alterLogo 的状态。

组件 Diff 流程

更新组件状态后,会触发组件的 Diff 流程,image 节点发生变化:

<view className="Logo" bindtap="{onTap}">
  + <image src="{reactLynxLogo}" className="Logo--react" /> - -
  <image src="{lynxLogo}" className="Logo--lynx" />
</view>

如下图所示,App 组件的 Diff 流程:

Component Diff Process

组件更新同步

组件 Diff 完成后会将更新信息同步到 Engine 线程,驱动后续 UI 变更。在 Trace 中点击 CallLepusMethod 事件,可以看到组件 Diff 完成后对应的 UI 更新过程。

Sync Diff To UI Thread

UI 更新

Engine 线程展示了 Element 树的更新过程。如下图所示,Element 树移除了一个节点,并新增了一个 image 节点。

Sync Diff To UI Thread

Element 更新完成后会重新排版和更新 UI。

Sync Diff To UI Thread

触发 useEffect 回调

Element 树更新完成后通知后台线程触发组件的 useEffect 回调。

为了在 Trace 中更清晰的看到 useEffect 回调的执行时机,使用 Trace API 添加自定义 trace。

useEffect(() => {
  console.info('Hello, ReactLynx');
}, []);

useEffect(() => {
  lynx.performance.profileMark('useEffect', {
    args: { alterLogo: `${alterLogo}` },
  });
}, [alterLogo]);
useEffect Callback
除非另有说明,本项目采用知识共享署名 4.0 国际许可协议进行许可,代码示例采用 Apache License 2.0 许可协议进行许可。