
一、为什么需要 Stage 模型早期鸿蒙用 FA 模型开发每个页面就是一个 Ability生命周期和窗口管理混在一起。手机场景下够用但一旦要适配手表、平板、PCFA 模型的局限就暴露了窗口大小变化没有统一处理机制不同设备的生命周期行为不一致资源调度也缺乏标准。Stage 模型的核心思路是把应用分成两层Window 层负责窗口管理尺寸、多窗口、最大化Ability 层负责业务逻辑。两层各司其职Ability 只关心页面内容该怎么渲染不管窗口怎么变化跨平台适配因此变得清晰。Ability 的生命周期只描述业务状态窗口的状态由 Window 层独立维护。二、断点体系设计适配多设备的第一步是建立断点体系。ArkUI 不需要设备类型检测只需要关注一个数字——窗口宽度。设备类型会骗人比如折叠屏展开 10 英寸、折叠时 6 英寸但deviceInfo.deviceType始终返回 “phone”。真正反映用户实际能看到的空间大小的是窗口宽度。业界常用的三级断点足够覆盖主流场景sm 600vp手机竖屏单栏布局。md600–1200vp手机横屏或小平板中等密度网格。lg≥ 1200vpPC 或大屏设备侧边栏 多列内容。拿到窗口宽度后调用一个工具函数就能得到当前断点代码不过十几行typeBreakpointsm|md|lgfunctiongetBreakpoint(width:number):Breakpoint{if(width600)returnsm// 手机if(width1200)returnmd// 平板returnlg// PC}这个函数放在一个工具模块里全局复用。每次aboutToAppear时重新计算保证窗口 resize 后布局也能正确更新。三、Ability 入口根据窗口宽度选择页面EntryAbility.ets是应用启动的必经之地在这里根据窗口宽度决定加载哪个页面。ArkUI 的条件加载能力让这一步变得简单——不需要写多套页面只需要在入口处做一次路径选择importUIAbilityfromohos.app.ability.UIAbilityimportWindowfromohos.windowexportdefaultclassEntryAbilityextendsUIAbility{onWindowStageCreate(windowStage:Window.WindowStage){constwinwindowStage.getMainWindowSync()constwidthpx2vp(win.getWindowProperties().windowWidth)// 768vp 是经验值基本能区分手机竖屏最大约 430vp和平板横屏constpagePathwidth768?pages/TwoColumnLayout:pages/IndexwindowStage.loadContent(pagePath,(err){if(err.code)console.error(loadContent failed:,JSON.stringify(err))})}}768vp 是一个经验值具体数值可根据产品实际需求调整。如果应用需要支持折叠屏建议在折叠切换时重新计算断点并触发页面重建。四、响应式布局的核心结构页面内部用State保存当前断点在aboutToAppear中获取窗口宽度并计算断点然后build()方法根据断点调用不同的Builder渲染内容。ArkUI 的条件渲染语法是if / else分支而非 CSS 的媒体查询——哪个分支返回就渲染哪个布局逻辑更直观。关键在于布局适配不在 CSS 里而在 TypeScript 逻辑里。这意味着断点切换时可以配合动画、数据加载等复杂行为而不只是单纯的样式变化。EntryComponentstruct ResponsiveLayout{Statebp:BreakpointsmStatewindowWidth:number360aboutToAppear(){constwinWindow.getLastWindowSync(getContext(this))this.windowWidthpx2vp(win.getWindowProperties().windowWidth)this.bpgetBreakpoint(this.windowWidth)}build(){Column(){this.TopBar()if(this.bpsm)this.MobileLayout()elsethis.DesktopLayout()}.width(100%).height(100%).backgroundColor(#F5F5F5)}// 顶部设备信息栏所有断点都显示BuilderTopBar(){Row({space:10}){Text().fontSize(18)Text(设备:${this.bp}宽度:${Math.round(this.windowWidth)}vp).fontSize(13).fontColor(#666666)Blank()Text([ 手机, 平板, PC][this.bpsm?0:this.bpmd?1:2]).fontSize(12).fontColor(#007DFF).padding({left:10,right:10,top:4,bottom:4}).backgroundColor(rgba(0,125,255,0.1)).borderRadius(12)}.width(100%).padding(14).backgroundColor(#FFFFFF)}// 手机单栏垂直堆叠BuilderMobileLayout(){Column({space:12}){this.Card( 首页,单栏垂直堆叠触摸友好适合手机竖屏。,#667eea)this.Card( 笔记,卡片点击展开全屏显示详情。,#4facfe)this.Card( 购物车,加减数量实时合计金额。,#43E97B)this.FeatureGrid(2)}.width(100%).padding(16)}// PC/平板双栏分栏BuilderDesktopLayout(){Row({space:0}){this.SideNav()Divider().vertical(true).height(100%)Column({space:16}){Row({space:12}){this.StatCard(总笔记,1,284,#667eea)this.StatCard(本周新增,42,#43E97B)this.StatCard(协作人数,8,#4facfe)}.width(100%)this.Card( 数据概览,双栏布局大屏展示更丰富的信息。,#f093fb)this.FeatureGrid(this.bpmd?3:4)}.layoutWeight(1).padding(20)}.width(100%)}}手机布局适合单栏垂直堆叠触摸交互友好的卡片从顶到底排列操作按钮放在底部固定区域。每张卡片都是全宽文字不换行不截断触控热区大不容易误操作。平板布局可以放宽约束在外层加maxWidth: 600限制内容宽度让内容区居中而不是撑满整个屏幕。网格列数增加到 3 列信息密度提升但仍保持良好的可读性。PC 布局用Row分成左右两栏左侧 160vp 固定宽度放导航右侧layoutWeight(1)占满剩余空间放主内容。主内容区进一步用Row展示统计数据卡片宽于普通卡片网格列数 4 列充分利用大屏空间。五、可复用组件三个Builder组件负责渲染具体内容可以在任何布局中复用代码量不大但使用频率最高卡片组件最通用上边加一条彩色装饰线区分不同内容区内部放标题和描述文字背景白色加圆角和投影。手机场景下全宽显示PC 场景下宽度受父容器约束一套样式自适应。统计卡片和普通卡片结构一致区别在于把标题字号加大、数字字号放到 26 加粗适合展示关键指标。PC 上可以把多个统计卡片排成一行手机上就垂直堆叠。网格组件通过GridGridItem实现根据传入的列数动态生成列模板。同样一组功能数据手机传 2 列、平板传 3 列、PC 传 4 列同样的数据源在不同断点下自动适配密度无需写三套代码。BuilderCard(title:string,content:string,color:string){Column({space:8}){Text(title).fontSize(16).fontWeight(FontWeight.Bold)Text(content).fontSize(13).fontColor(#666666).lineHeight(20)}.width(100%).padding(16).backgroundColor(#FFFFFF).borderRadius(12).shadow({radius:4,color:rgba(0,0,0,0.06),offsetX:0,offsetY:2}).border({top:3,color:color})}BuilderStatCard(label:string,value:string,color:string){Column({space:6}){Text(label).fontSize(13).fontColor(#888888)Text(value).fontSize(26).fontWeight(FontWeight.Bold)}.width(100%).padding(16).backgroundColor(#FFFFFF).borderRadius(12).shadow({radius:4,color:rgba(0,0,0,0.06),offsetX:0,offsetY:2}).border({top:3,color:color})}BuilderFeatureGrid(cols:number){constfeatures[{icon:,name:ArkUI},{icon:,name:ArkTS},{icon:,name:分布式},{icon:,name:TaskPool},{icon:,name:存储},{icon:,name:网络},]Column({space:8}){Text(功能模块).fontSize(14).fontWeight(FontWeight.Bold).width(100%).margin({left:4})Grid(){ForEach(features,(f){GridItem(){Column({space:6}){Text(f.icon).fontSize(24)Text(f.name).fontSize(13)}.width(100%).height(100%).justifyContent(FlexAlign.Center).backgroundColor(#FAFAFA).borderRadius(10).onClick(()console.info(功能:${f.name}))}})}.columnsTemplate(1fr.repeat(cols).trim()).rowsTemplate(1fr 1fr).width(100%).height(160).columnsGap(8).rowsGap(8)}.width(100%).padding(16).backgroundColor(#FFFFFF).borderRadius(12)}六、侧边导航组件PC 场景下侧边导航栏是标配。和手机底部的 TabBar 不同侧边导航通常更宽可以放下图标加文字还可以加选中态的彩色指示条。导航项的数据结构是数组用ForEach渲染点击时更新当前选中状态BuilderSideNav(){Column({space:4}){ForEach([{icon:,label:首页,active:true},{icon:,label:笔记,active:false},{icon:,label:购物,active:false},{icon:⚙️,label:设置,active:false},],(item){Row({space:10}){Text(item.icon).fontSize(18)Text(item.label).fontSize(14).fontColor(item.active?#007DFF:#666666)if(item.active){Blank()Text().width(3).height(20).backgroundColor(#007DFF).borderRadius(2)}}.width(100%).height(48).padding({left:16,right:16}).borderRadius(8).backgroundColor(item.active?rgba(0,125,255,0.08):transparent).onClick(()console.info(导航:${item.label}))})}.width(160).height(100%).padding(12).backgroundColor(#FFFFFF)}七、PC 专属交互PC 上的交互比手机丰富得多。鼠标悬停Hover是 PC 独有的——鼠标移入时触发状态变化可以做颜色加深、阴影增强等视觉反馈比手机触摸更早一步给用户暗示。ArkUI 通过onHover回调处理传入布尔值区分进入和离开配合animateTo可以做平滑的过渡动画StatehoverText:string将鼠标移入蓝色区域Column().width(200).height(80).backgroundColor(#667eea).borderRadius(12).onHover((isHover:boolean){animateTo({duration:200},(){this.hoverTextisHover?✅ 鼠标已悬停:将鼠标移入蓝色区域})}).child(Text(this.hoverText).fontSize(13).fontColor(#FFFFFF).width(100%).textAlign(TextAlign.Center)).alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)键盘快捷键是 PC 效率工具的核心。CtrlS 保存、CtrlN 新建、CtrlF 搜索、Escape 关闭——这些约定俗成的快捷键可以显著提升 PC 用户的操作效率在表单类应用中尤其重要。ArkUI 目前通过监听键盘事件处理后续版本会提供更完整的快捷键注册机制。八、窗口监听与动态适配PC 应用需要实时响应窗口大小的改变。通过window.on(windowSizeChange)可以监听窗口变化在回调里重新计算断点并更新状态变量布局会自动重建aboutToAppear(){constwinWindow.getLastWindowSync(getContext(this))// 注册窗口变化监听win.on(windowSizeChange,(size){this.windowWidthpx2vp(size.width)this.bpgetBreakpoint(this.windowWidth)})}aboutToDisappear(){// 取消监听防止内存泄漏constwinWindow.getLastWindowSync(getContext(this))win.off(windowSizeChange)}这里有一个常见的坑监听注册后没有取消。每次页面重建时aboutToAppear都会被调用如果不在aboutToDisappear中清理监听器会越积越多内存占用持续增长直到应用崩溃。九、实战建议适配的核心原则是从窗口宽度出发而非设备类型。windowWidth 768这种判断直接反映用户实际能看到的空间大小而非设备标称形态。设备类型会骗人但窗口宽度不会。组件设计应遵循最大公约数原则优先使用所有平台都支持的交互点击、长按PC 特有的 Hover 和右键菜单用条件分支增强不确定时优先用点击而非手势。断点变化时用animateTo包裹布局属性过渡避免内容闪烁。页面路径选择放在 Ability 入口层组件内部只做布局适配——这个分层原则值得坚持。页面路径是架构决策布局适配是表现层细节两者在不同层级处理代码更清晰维护成本也更低。