首屏直出(IFR) v0.5

首屏直出(Instant First-Frame Rendering,IFR)会在 Lynx loadTemplate 期间、后台线程启动之前绘制真实内容,去掉原先等待后台启动、首次 Vue 渲染和 IPC 造成的白屏。

本页是产品论点——怎么快、以及为什么是架构优势。代价账本、gzip/TTI、 代价空间图与 FCP 数字见 IFR 性能数据 · 统一矩阵

1. 怎么实现快

没有 IFR 时,可见内容必须等后台线程:

主线程:   空页面 ───────────────────────▶ 应用 ops ─▶ 绘制
后台线程:          启动 ─▶ 渲染 ─▶ IPC ─┘

开启 IFR 后,主线程 bundle 携带 Vue 运行时和应用。Vue 在 loadTemplate 内同步渲染,后台线程并行启动:

主线程:   loadTemplate ─▶ 渲染 ─▶ 绘制
后台线程:               启动 ─▶ 渲染 ─▶ hydration

让这次同步绘制「够便宜、能上线」靠两层:

作用
何时画IFR — loadTemplate 里主线程先画;后台启动重叠
画多便宜VDOM:enableIFR 自带元素模板 · Vapor:template()REGISTER_TREE / CLONE_TREE(不用 ET)

裸 IFR(无 ET)在主线程上仍接近整条 create 路径。所以 VDOM 默认 IFR + 元素模板 一起开——数字见 IFR 性能数据 的策略阶梯。

2. 为什么是架构优势

IFR 不是「任何 harness 里都更快的首帧」。它消掉的是真实线程边界造成的白屏。单进程 / 同 isolate 的 bench 会很平——没有可藏的 IPC 等待。在 Lynx for Web(以及 native 双线程)上,那段白屏是真的,所以 IFR 会体现为产品 FCP。

这也是为什么统一矩阵(Worker 边界)和 IFR 示例 harness 回答的是不同问题:一个量收益,一个量账单。


结论

怎么快?loadTemplate 里主线程先画;后台启动重叠。规模上要便宜的 create 路径(ET / Vapor 树)。
代价双 bundle · gzip 约 · 主线程同步工作——见 IFR 性能数据
架构只有真实线程边界造成的白屏,才能被 IFR 消掉。
怎么开VDOM:enableIFR: true(自带 ET)。规模上别裸开 IFR。始终 mount 出真正外壳——挡住 fetch,不要跳过 app.mount()
何时不开响应前无物可画;Web 上大 bundle + 重 CPU 节流(先实测);或未实测就扛不住约 2× gzip。

开启

lynx.config.ts
import { defineConfig } from '@lynx-js/rspeedy'
import { pluginVueLynx } from 'vue-lynx/plugin'

export default defineConfig({
  plugins: [
    pluginVueLynx({
      enableIFR: true, // VDOM:同时开启元素模板
      // vapor: true,  // 可选 — Vapor IFR 走树 clone,不用 ET
    }),
  ],
})
Hydration两线程同一应用 + 相同初始数据;结构不一致会丢掉 IFR 收益(开发环境打 log),不影响正确性。
首屏约束渲染确定性;副作用放组合式 API 生命周期(主线程抑制);Options API mounted() 尚未抑制。
Shell IFRfetch 驱动页面仍应 mount,并在主线程画出有意义的外壳——用例如 useQuery({ enabled: !isIfrMainThread() }) 关掉网络请求,而不是整段跳过 app.mount()。跳过 mount 只会留下更大的 IFR bundle,却拿不到首屏收益。
二分 ETenableIFR: true, enableElementTemplates: false — 仅调试,不是性能旋钮。
Vapor实验性(Vue 3.6 预发布)。见 Vapor 模式

策略阶梯、真实线程 FCP、代价空间、示例 gzip/TTI、native: IFR 性能数据 · 产品 FCP 矩阵: 统一矩阵