在上一篇文章中,我们探讨了鸿蒙的基础组件、状态管理和资源管理。今天,我们来深入项目的实际开发场景,聊聊一多开发(一次开发,多端部署)ArkTS/ArkUI 的核心概念以及面试中高频出现的布局与状态问题。


8. 你项目中的一多开发是如何实现的?

核心思路:自适应布局 + 断点系统 + 多态组件

在实战中,“一多开发”并不是为每种设备单独写一套代码,而是通过自适应布局来响应当前设备的屏幕尺寸和方向。

  • 布局适配:优先使用 FlexColumnRowStack等弹性布局,配合 layoutWeight分配空间,避免写死宽高。

  • 媒体查询(MediaQuery):针对大屏(如平板折叠屏)和小屏(手机)展示不同布局。例如,手机端单列展示,平板端双列展示。

  • 栅格系统(GridRow/GridCol):常用于表单、列表页,通过断点(xs, sm, md, lg)控制每行的列数。

  • 多态组件:使用 @MediaQuery装饰器或 useBreakpointAPI 获取当前屏幕宽度,动态改变组件排列方式。


9. 什么是 ArkTS?什么是 ArkUI?

这是两个不同层面的概念:

  • ArkTS

    • 定义:是鸿蒙生态的应用开发语言。

    • 本质:它是 TypeScript(TS)的超集。在 TS 的基础上,扩展了静态类型检查声明式 UI 语法状态管理等能力。

    • 作用:负责处理业务逻辑、数据处理和算法。

  • ArkUI

    • 定义:是鸿蒙的声明式 UI 框架

    • 本质:一套用于构建用户界面的框架体系。

    • 作用:负责将数据和状态渲染成界面。

一句话总结:ArkTS 是“大脑”(逻辑语言),ArkUI 是“画笔”(UI 框架)。


10. 请说下你使用过鸿蒙中哪些装饰器?并说明这些装饰器的作用?

在 ArkTS 中,装饰器(Decorator)是连接数据与 UI 的桥梁。常见的可分为以下几类:

  1. 状态管理类

    • @State:组件内部状态。当其修饰的变量变化时,当前组件 UI 刷新。

    • @Prop:父子单向通信。父组件传值给子组件,子组件不可反向修改。

    • @Link:父子双向通信。父子组件共享同一份数据引用,一方改两边都变。

    • @ObjectLink/ @Observed:嵌套对象的状态同步。

  2. 应用级状态类

    • @StorageLink/ @StorageProp:与 AppStorage 联动,实现全局状态管理。

  3. 组件复用类

    • @Component:标记这是一个自定义组件。

    • @Builder:定义可复用的 UI 结构片段(类似局部模板)。

    • @Entry:标记页面的入口组件。


11. 什么场景(你项目中哪里)使用 Flex 布局?使用 Flex 布局会存在什么问题?

  • 使用场景

    • 导航栏/标签栏:横向排列的 Tab 或底部菜单。

    • 列表项:左侧图标 + 右侧文字的列表排版。

    • 表单区域:一行内放置多个输入框或标签。

    • 流式布局:元素需要自动换行的情况。

  • 存在的问题

    1. 权重分配陷阱:过度依赖 layoutWeight有时会导致父容器尺寸计算延迟,引起闪烁。

    2. 默认对齐方式:Flex 默认不换行且靠主轴起点对齐,如果不显式设置 flexWrap: FlexWrap.Wrap,在小屏设备上容易出现内容溢出

    3. 高度塌陷:如果主轴是水平方向(direction: FlexDirection.Row),且子元素没有明确高度,交叉轴(高度)可能会压缩。


12. 什么场景(你项目中哪里)使用 Stack 布局?

Stack(层叠布局)用于将组件按照层级堆叠在一起。

  • 典型场景

    • 绝对定位覆盖:比如图片上叠加文字(商品封面图 + 标题)。

    • 悬浮按钮(FAB):在页面右下角固定一个按钮,覆盖在列表之上。

    • 遮罩层(Overlay):弹窗背景半透明遮罩,上层显示弹窗内容。

    • 引导页指示点:底部的小圆点指示当前在第几页。


13. 定位有哪些?区别是啥?什么场景下使用定位?

在 ArkUI 声明式开发中,"定位"不完全等同于 CSS 的 position枚举,更准确的说法是组件相对于父容器/兄弟组件的摆放方式。常用的有四种思路:

.offset({x, y})—— 相对偏移
Text('原位置')
  .width(100).height(40)
  .backgroundColor('#90CAF9')

Text('offset 偏移')
  .width(100).height(40)
  .offset({ x: 120, y: 60 }) // 在原位置基础上向右120、向下60
  • 特点:在原文档流位置基础上做偏移,原位置仍保留占位,不会影响兄弟组件的布局测算。

  • 场景:微调按钮上的红点、Tooltip 气泡、图标旁的小标签——"我想让它挪一下,但别打乱别人"。

.position({x, y})—— 绝对定位
Stack() {
  Image($r('app.media.bg'))
  Text('99+')
    .position({ x: 32, y: 4 }) // 以父容器(Stack)左上角为原点
    .backgroundColor(Color.Red)
}
  • 特点:以父组件内容区左上角为坐标原点放置子组件,脱离文档流,父容器如果是 Row/Column/Flex,设了 .position()的子组件不参与父容器的排版测算(不占位)。

  • 场景:铃铛右上角的红点徽标、图片上叠加文字、悬浮按钮、遮罩层内的弹窗主体。

💡 搭配 .markAnchor({x, y})可以改变"自身哪个点"去对齐那个坐标——比如想让红点的右上角贴在铃铛右肩,而不是默认的左上角。

RelativeContainer+ alignRules—— 约束定位
RelativeContainer() {
  Text('A').id('a').alignRules({
    top: { anchor: '__container__', align: VerticalAlign.Top },
    left: { anchor: '__container__', align: HorizontalAlign.Start }
  })
  Text('B').alignRules({
    top: { anchor: 'a', align: VerticalAlign.Bottom },
    left: { anchor: 'a', align: HorizontalAlign.Start }
  })
}
  • 特点:通过 id+ alignRules用"锚点"描述组件之间的相对关系,不需要写死 x/y,适配性远好于 .position()

  • 场景:复杂表单、仪表盘、多个元素彼此有"谁在谁上面/左边"关系的界面。是鸿蒙官方推荐的精确关系布局方案,比绝对定位更适合多尺寸设备。

Stack+ Alignment—— 层叠定位
Stack({ alignContent: Alignment.TopEnd }) {
  Image($r('app.media.banner'))
  Badge({ count: 3 }) // 自动贴右上角
}
  • 特点:子组件按"层"堆叠,通过 AlignmentalignSelf决定每层在父容器里的位置。

  • 场景:封面图+标题、卡片+角标、全屏遮罩+居中弹窗。


一张表理清区别

方式

是否脱流

参考系

多端适配友好度

.offset()

否,原位置保留

自身原位置

⭐⭐⭐ 好

.position()

是,不占位

父容器内容区左上角

⭐ 差(写死坐标)

RelativeContainer

否,按约束算

锚点组件 / 父容器

⭐⭐⭐⭐ 最佳

Stack+ Alignment

否,层叠

父容器九宫格方位

⭐⭐⭐ 中等

📌 面试踩坑点:很多人把 ArkUI 的 .position()直接等同于 CSS 的 position: absolute,但其实 ArkUI 没有 static/relative/absoulte/fixed这套枚举(类 CSS 的 position字符串写法只在方舟 UI 框架/元服务类场景出现,声明式 ArkTS 主流是 API 这套)。被问到"鸿蒙的定位有几种"时,按「offset → position → RelativeContainer → Stack」四条答,比套 CSS 那套更贴鸿蒙面试官的预期。


14. layoutWeight 属性作用是什么?什么时候使用?

  • 作用

    layoutWeight用于在线性布局(Column/Row/Flex)中分配剩余空间。它决定了组件在主轴上占据的相对比例

    • Column中,控制高度占比。

    • Row中,控制宽度占比。

  • 使用时机

    • 当容器有多余空间需要分配给子组件时。

    • 经典案例:底部 Tab 栏平分宽度。给每个 Tab 按钮设置 layoutWeight(1),它们就会自动等宽撑满底部。


15. 如何让一个组件在页面中既水平又垂直居中显示?

这是面试必考题,有三种常用方案:

方案一:使用 Flex布局(推荐,最灵活)

设置容器的主轴和交叉轴对齐方式均为居中。

Column() {
  Text('居中内容')
}.width('100%').height('100%').justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)

方案二:使用 Stack布局(最直观)

利用层叠布局的特性,将子组件堆叠在父容器的中心原点。

Stack() {
  Text('居中内容')
}.width('100%').height('100%').alignContent(Alignment.Center)

方案三:使用 Blank组件(巧妙解法)

利用空白填充物把组件“挤”到中间。

Column() {
  Blank() // 自动填满上方空间
  Text('居中内容')
  Blank() // 自动填满下方空间
}.width('100%').height('100%')

16. let 和 const 和 @State 的区别?

这三个概念分别处于语言基础层框架状态层

  1. let(可变变量)

    • ES6 语法。

    • 值可以改变,但改变时不会触发 UI 刷新

    • 仅用于存储纯逻辑计算的临时数据。

  2. const(常量)

    • ES6 语法。

    • 一旦赋值不可更改,试图修改会报错。

    • 同样不具备 UI 响应能力

  3. @State(状态装饰器)

    • ArkTS 特有装饰器。

    • 本质上它也是一个变量,但当它修饰的变量值发生改变时,框架会自动检测变化并触发依赖它的 UI 重新渲染

总结let/const是纯粹的编程语言特性,负责数据的存储;@State是框架提供的机制,负责数据的响应式绑定

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐