移动端布局
移动应用运行在手机、折叠屏、平板以及可调整大小的 iPad 窗口上。靠一条规则,这件事才仍然可控:下面每一层只做一个决定,而且没有一层去做另一层的决定。功能屏幕从不过问自己在哪个平台上,也不过问窗口有多宽。


各层
路由。 根堆栈就是布局状态:选中了什么、推入了什么、哪张工作表开着。旋转、折叠或调整大小时,同一个堆栈会以不同方式渲染。没有任何东西记住页面曾经在哪一列。每条路由用一个预设声明自己是什么(
pageRoute、sheetRoute、modalRoute、immersiveRoute);预设负责停靠档位、圆角、顶栏外观和 Android 回退。参见移动端导航。工作区外壳。
features/layout负责位置与宽度,宽度等级来自layout.ts:- 展开:侧边栏、内容与检查器三列。
- 中等(宽 600–720pt):侧边栏挨着列表,并浮在页面之上。
- 紧凑(任一方向不足 600pt):侧边栏是根,页面以推入方式打开。
- 检查器列放不下时,检查器是一张工作表。
工作区的标签页、停靠栏和工具栏位于
features/layout/workspace。嵌在工作表或弹出层里的堆栈,以及在自身路由下渲染的屏幕,也来自这里。功能屏幕。 屏幕按角色摆放内容。它绘制一个
AppBar(表单工作表则用SheetHeader),渲染自己的页面,用<Inspector>声明检查器窗格,并用showInspector()请求显示它们。外壳决定采用列还是工作表。在这里按宽度或平台去选择路由、组件、顶栏选项或内边距,正是这套结构要防止的错误。设计系统。 每个窗格、页面、工作表和模态框都有一个
AppBar,两个平台上的约定相同;工作表或模态框会传入onClose。PaneContext告诉栏自己位于哪一列,于是栏加上该列的控件,例如侧边栏开关。内容与栏相接的位置来自useBarInset。界面框架只在这里按平台分叉。原生。 在 iOS 26 上,这些列是各自带一条导航栏的
UISplitViewController列。在 Android 以及 iPadOS 18–25 上,外壳根据窗口和铰链度量,用 JS 画出同样的列。Android 从不显示原生堆栈顶栏;每一条栏都在组件树内绘制。
平台分叉位于第 1、2、4 层,以 .ios 和 .android 文件的形式存在。
陷阱
- iPadOS 18–25 运行的是 JS 列,而 iOS 的部署目标是 18。JS 列路径里看起来只针对 Android 的代码,在这些 iPad 上仍然会执行。
- Android 会把
Home渲染两次:一次在侧边栏列中,一次作为内容堆栈的根。侧边栏那一份永远不是处于焦点的路由,因此以焦点为门控的 hook(例如返回处理)还必须接受usePaneRole() === "sidebar"。 - 模态框和全屏页面呈现在列之上,渲染时没有窗格角色,因此没有列的界面框架。
- 在 iOS 上,每个检查器窗格都是独立的原生屏幕,只有当前活动的那个被挂接。即使用透明度隐藏,窗格的原生栏、工具栏和滚动视图仍在窗口中,UIKit 会把它们画在可见窗格上面。
- 从检查器工作表打开的路由必须等工作表关闭,这通过
InspectorSheetContext完成。在正在关闭的工作表之上呈现,会在 iOS 上与其关闭发生竞态。