鸿蒙 6.1 API 23 开发坑系列篇 5:arkui.observer UI 观察器坑——uiObserver namespace 真名不是 observer + on type 是 string literal 不是 enum 根因

本文是「鸿蒙 6.1 API 23 开发坑系列」第 5 篇(ArkUI 桶第 5 篇)。本篇讲 @ohos.arkui.observer namespace(API 11+,鸿蒙 6.1 API 23 基座)——UI 观察器 uiObserver namespace + on(type, callback)/off(type, callback) + 7 种 type + ScrollEventInfo/NavDestinationInfo鸿蒙坑根因:① namespace 真名是 uiObserver 不是 observerimport { observer } from 编译错 has no exported member,必须 import uiObserver fromdefault import不是 named import));② on(type, callback) 的 type 参数是 string literal"scrollEvent")不是 enum(ScrollEventType);③ ScrollEventInfo/ObserverOptions/NavDestinationInfo/NavDestinationState嵌套在 namespace uiObserver 里不是顶层 export(import { ScrollEventInfo } 编译错,必须 uiObserver.ScrollEventInfo 命名空间访问);④ ScrollEventInfo 没有 type 属性(React scroll event 有 type,鸿蒙没有,真属性是 id/uniqueId/offset/triggerOffset/observableScrollableTotalRange);⑤ on 支持 7 种 type string literal:scrollEvent/navDestinationUpdate/routerPageUpdate/densityUpdate/willDraw/didLayout/tabContentUpdate/navDestinationSwitch

一、开篇:鸿蒙 uiObserver 不是 React addEventListener,是「namespace 顶层函数 on/off」

你写 React 时,UI 事件监听用 addEventListener(type 是 enum/string,回调传 Event 对象):

// React addEventListener:type 是 string/enum,回调传 Event 对象
element.addEventListener('scroll', (event: Event) => {  // ❌ React type 是 string 'scroll'
  console.log(event.type)  // ❌ React Event 有 type 属性('scroll')
})

你写鸿蒙 ArkTS 时,UI 观察器用 uiObserver.on(type, callback)(namespace 顶层函数,type 是 string literal):

// ArkTS uiObserver.on:namespace 顶层函数,type 是 string literal 不是 enum
import uiObserver from '@ohos.arkui.observer'  // ✅ default import(不是 { uiObserver })

uiObserver.on('scrollEvent', (info: uiObserver.ScrollEventInfo) => {  // ✅ type 是 string literal 'scrollEvent'
  // ✅ ScrollEventInfo 嵌套在 namespace uiObserver 里(不是顶层 export)
  // ✅ ScrollEventInfo 没有 type 属性(React Event 有 type,鸿蒙没有)
  console.log(`uniqueId: ${info.uniqueId}, offset: ${info.offset}`)  // ✅ 真属性是 uniqueId/offset
})
// 鸿蒙坑根因:namespace 真名是 uiObserver 不是 observer,on type 是 string literal 不是 enum

React addEventListener vs 鸿蒙 uiObserver.on 的区别:React 把事件监听当 DOM 方法(element.addEventListener('scroll', cb),type 是 string/enum,回调传 Event 对象有 type 属性),ArkTS 把 UI 观察器当 namespace 顶层函数(uiObserver.on('scrollEvent', cb),type 是 string literal 不是 enum,回调传 ScrollEventInfo 嵌套在 namespace 里没有 type 属性)。根因不是 DOM 方法是 namespace 顶层函数——鸿蒙 uiObserver.on 的 type 是 string literal,ScrollEventInfo 嵌套在 namespace 里没有 type 属性。

二、根因:鸿蒙 arkui.observer 的五个绑定机制

鸿蒙 @ohos.arkui.observer namespace(API 11+)核心导出 uiObserver namespace(default export)+ on(type, callback)/off(type, callback) 顶层函数 + ScrollEventInfo/ObserverOptions/NavDestinationInfo/NavDestinationState/ScrollEventType 嵌套类型。绑定机制来自五重根因。

机制 1:namespace 真名是 uiObserver 不是 observer——default import 不是 named import

鸿蒙坑根因:namespace 真名是 uiObserver,且是 default export 不是 named export:

// ❌ 鸿蒙坑:import { observer } from 编译错(namespace 真名是 uiObserver 不是 observer)
import { observer } from '@ohos.arkui.observer'  // ❌ has no exported member 'observer'
// ❌ import { uiObserver } from '@ohos.arkui.observer'  // ❌ 也编译错(default export 不是 named export)

// ✅ 正确用法:default import(uiObserver 是 default export 不是 named export)
import uiObserver from '@ohos.arkui.observer'  // ✅ default import
// 鸿蒙坑根因:namespace 真名是 uiObserver 不是 observer,且是 default export 不是 named export

namespace 真名坑根因@ohos.arkui.observer.d.ts 声明是 declare default namespace uiObserver(default namespace export,真名 uiObserver 不是 observer),所以 import { observer } 触发 has no exported member 'observer' 编译错,import { uiObserver } 触发 has no exported member 'uiObserver'(default export 不能 named import)。正确用法是 import uiObserver from(default import)。鸿蒙坑:namespace 真名 uiObserver 不是 observer(文件名 observer 但 namespace 名 uiObserver),且是 default export 必须 default import。

机制 2:on 的 type 参数是 string literal 不是 enum——“scrollEvent” 不是 ScrollEventType

鸿蒙坑根因:on(type, callback) 的 type 参数是 string literal,不是 enum:

// ❌ 鸿蒙坑:on 的 type 传 enum 编译错(type 是 string literal 不是 enum)
import uiObserver from '@ohos.arkui.observer'

// ❌ 编译错:Argument of type 'ScrollEventType' is not assignable to parameter of type 'string'
uiObserver.on(ScrollEventType.SCROLL_START, callback)  // ❌ type 不是 enum 是 string literal

// ✅ 正确用法:type 传 string literal 'scrollEvent'(不是 enum ScrollEventType)
uiObserver.on('scrollEvent', callback)  // ✅ type 是 string literal 'scrollEvent'
// 鸿蒙坑根因:on 的 type 是 string literal("scrollEvent")不是 enum(ScrollEventType)

type string literal 坑根因uiObserver.on(type: string, callback: AsyncCallback<ScrollEventInfo>): void 的 type 参数类型是 string(string literal),不是 enum ScrollEventType鸿蒙坑:传 enum ScrollEventType.SCROLL_START 触发 Argument of type 'ScrollEventType' is not assignable to parameter of type 'string' 编译错——必须传 string literal 'scrollEvent'ScrollEventType enum(SCROLL_START=0/SCROLL_STOP=1)是 ScrollEventInfo.type 属性的值类型(但 ScrollEventInfo 实际没有 type 属性,见机制 4),不是 on 的 type 参数。

机制 3:ScrollEventInfo/ObserverOptions/NavDestinationInfo 嵌套在 namespace 里——不是顶层 export

鸿蒙坑根因:ScrollEventInfo/ObserverOptions/NavDestinationInfo/NavDestinationState嵌套在 namespace uiObserver 里,不是顶层 export:

// ❌ 鸿蒙坑:import { ScrollEventInfo } from 编译错(嵌套在 namespace uiObserver 里不是顶层 export)
import { ScrollEventInfo, ObserverOptions, NavDestinationInfo } from '@ohos.arkui.observer'  // ❌ has no exported member

// ✅ 正确用法:用 uiObserver.ScrollEventInfo 命名空间访问(嵌套在 namespace uiObserver 里)
import uiObserver from '@ohos.arkui.observer'

uiObserver.on('scrollEvent', (info: uiObserver.ScrollEventInfo) => {  // ✅ uiObserver.ScrollEventInfo
  const options: uiObserver.ObserverOptions = { id: 'scrollTarget' }  // ✅ uiObserver.ObserverOptions
  uiObserver.on('navDestinationUpdate', (navInfo: uiObserver.NavDestinationInfo) => {  // ✅ uiObserver.NavDestinationInfo
  }
})
// 鸿蒙坑根因:ScrollEventInfo/ObserverOptions/NavDestinationInfo 嵌套在 namespace uiObserver 里不是顶层 export

嵌套 namespace 坑根因@ohos.arkui.observer.d.tsdeclare default namespace uiObserver { export interface ScrollEventInfo { ... } }——ScrollEventInfo/ObserverOptions/NavDestinationInfo/NavDestinationState/ScrollEventType 都声明在 namespace uiObserver { } 块内(嵌套 export),不是顶层 export。鸿蒙坑import { ScrollEventInfo } 触发 has no exported member 'ScrollEventInfo' 编译错——必须用 uiObserver.ScrollEventInfo 命名空间访问。

机制 4:ScrollEventInfo 没有 type 属性——真属性是 id/uniqueId/offset/triggerOffset

鸿蒙坑根因:ScrollEventInfo 没有 type 属性(React Event 有 type,鸿蒙没有):

// ❌ 鸿蒙坑:ScrollEventInfo 没有 type 属性(React Event 有 type,鸿蒙没有)
import uiObserver from '@ohos.arkui.observer'

uiObserver.on('scrollEvent', (info: uiObserver.ScrollEventInfo) => {
  // ❌ 编译错:Property 'type' does not exist on type 'ScrollEventInfo'
  console.log(info.type)  // ❌ ScrollEventInfo 没有 type 属性(React Event 有 type,鸿蒙没有)

  // ✅ 正确用法:用真属性 id/uniqueId/offset/triggerOffset/observableScrollableTotalRange
  console.log(`uniqueId: ${info.uniqueId}`)  // ✅ uniqueId 滚动组件唯一 id
  console.log(`offset: ${info.offset}`)  // ✅ offset 滚动偏移量
  console.log(`triggerOffset: ${info.triggerOffset}`)  // ✅ triggerOffset 触发偏移量
  console.log(`observableScrollableTotalRange: ${info.observableScrollableTotalRange}`)  // ✅ 可观察滚动总范围
})
// 鸿蒙坑根因:ScrollEventInfo 没有 type 属性,真属性是 id/uniqueId/offset/triggerOffset

ScrollEventInfo 无 type 坑根因uiObserver.ScrollEventInfo 的真属性是 id: ResourceStr(组件 id)、uniqueId: string(唯一 id)、offset: number(滚动偏移量)、triggerOffset: number(触发偏移量)、observableScrollableTotalRange: ObservableScrollableTotalRange(可观察滚动总范围)。鸿蒙坑:React Eventtype 属性('scroll'),鸿蒙 ScrollEventInfo 没有 type 属性——传 info.type 触发 Property 'type' does not exist on type 'ScrollEventInfo' 编译错。ScrollEventType enum(SCROLL_START=0/SCROLL_STOP=1)不是 ScrollEventInfo.type 属性的值类型(ScrollEventInfotype 属性),是别处的 enum。

机制 5:on 支持 7 种 type string literal——scrollEvent/navDestinationUpdate 等

鸿蒙坑根因:on 支持 7 种 type string literal,每个对应不同 Info 类型:

// ✅ on 支持 7 种 type string literal(每个对应不同 Info 类型)
import uiObserver from '@ohos.arkui.observer'

// ✅ 7 种 type string literal:
uiObserver.on('scrollEvent', (info: uiObserver.ScrollEventInfo) => { })  // ✅ 滚动事件
uiObserver.on('navDestinationUpdate', (info: uiObserver.NavDestinationInfo) => { })  // ✅ NavDestination 路由更新
uiObserver.on('routerPageUpdate', (info: uiObserver.RouterPageInfo) => { })  // ✅ Router 页面更新
uiObserver.on('densityUpdate', (info: uiObserver.DensityInfo) => { })  // ✅ Density 更新
uiObserver.on('willDraw', () => { })  // ✅ 将要绘制(无 Info 参数)
uiObserver.on('didLayout', () => { })  // ✅ 布局完成(无 Info 参数)
uiObserver.on('tabContentUpdate', (info: uiObserver.TabContentInfo) => { })  // ✅ TabContent 更新
// ✅ 还有 navDestinationSwitch(API 11+,NavDestination 切换)
// 鸿蒙坑根因:on 支持 7 种 type string literal,每个对应不同 Info 类型

7 种 type string literal 坑根因uiObserver.on(type: string, callback) 的 type 支持 7 种 string literal:'scrollEvent'(滚动事件,回调传 ScrollEventInfo)、'navDestinationUpdate'(NavDestination 路由更新,回调传 NavDestinationInfo)、'routerPageUpdate'(Router 页面更新,回调传 RouterPageInfo)、'densityUpdate'(Density 更新,回调传 DensityInfo)、'willDraw'(将要绘制,无 Info 参数)、'didLayout'(布局完成,无 Info 参数)、'tabContentUpdate'(TabContent 更新,回调传 TabContentInfo)、'navDestinationSwitch'(API 11+,NavDestination 切换)。鸿蒙坑:React addEventListener type 是 DOM 事件名('scroll'/'click'),鸿蒙 uiObserver.on type 是 7 种 string literal('scrollEvent' 不是 'scroll',驼峰命名带 Event 后缀)。

三、真机配图:鸿蒙 arkui.observer UI 观察器坑——uiObserver namespace + on/off

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

真机配图展示鸿蒙 arkui.observer UI 观察器坑:

  • 初始态:鸿蒙 6.1 arkui.observer UI 观察器坑标题,滚动目标组件(绿框 id=scrollTarget,5 行滚动内容),场景1~5 卡片(namespace 真名/on scrollEvent/on+ObserverOptions/off scrollEvent/on navDestinationUpdate),要点说明
  • namespace + on scrollEvent 态:点击「① 验证 namespace 真名」+「② 验证 on scrollEvent」+「③ 验证 on + ObserverOptions」按钮,显示「✅ uiObserver namespace 真名不是 observer」+「✅ uiObserver.on(“scrollEvent”, callback) 监听滚动事件验证成功」+「✅ 带 ObserverOptions 监听指定 id 验证成功」——namespace 真名 + on type string literal + ObserverOptions 验证
  • 滑后态:下滑后显示场景4(off scrollEvent)+ 场景5(on navDestinationUpdate)+ 要点说明
  • off + navDestinationUpdate 怺态:点击「⑦ 验证 off scrollEvent」+「⑤ 验证 on navDestinationUpdate」按钮,显示「✅ uiObserver.off(“scrollEvent”, callback) 取消监听验证成功」+「✅ uiObserver.on(“navDestinationUpdate”, callback) 监听 NavDestination 路由验证成功」——off callback 同引用 + navDestinationUpdate 验证

四、真解法:鸿蒙 arkui.observer 的四个场景

场景 1:uiObserver namespace default import + on(‘scrollEvent’)——90% 场景首选

uiObserver on 监听用 import uiObserver from + uiObserver.on('scrollEvent', callback)

// ✅ 场景 1:uiObserver namespace default import + on('scrollEvent')(API 11,90% 场景首选)
import uiObserver from '@ohos.arkui.observer'  // ✅ default import(不是 { uiObserver })

@Entry
@Component
struct Index {
  aboutToAppear() {
    // ✅ on('scrollEvent', callback) 监听滚动事件——type 是 string literal 不是 enum
    uiObserver.on('scrollEvent', (info: uiObserver.ScrollEventInfo) => {  // ✅ uiObserver.ScrollEventInfo 命名空间访问
      // ✅ ScrollEventInfo 没有 type 属性,真属性是 uniqueId/offset/triggerOffset
      console.log(`uniqueId: ${info.uniqueId}, offset: ${info.offset}`)  // ✅ 真属性
    })
  }
  build() { Column({ space: 8 }) { Text('demo') } }
}
// uiObserver namespace default import + on('scrollEvent'):90% 场景首选,type 是 string literal

鸿蒙 arkui.observer API 真名坑import uiObserver from '@ohos.arkui.observer'(default import 不是 named import,namespace 真名 uiObserver 不是 observer);uiObserver.on(type: string, callback: AsyncCallback<ScrollEventInfo>): void namespace 顶层函数(type 是 string literal 不是 enum);uiObserver.off(type: string, callback?: AsyncCallback<T>): void 取消监听(callback 可选,不传取消所有);ScrollEventInfo/ObserverOptions/NavDestinationInfo 嵌套在 namespace uiObserver 里(用 uiObserver.ScrollEventInfo 命名空间访问);SysCap SystemCapability.ArkUI.ArkUI.Full@crossplatform 跨平台;@atomicservice 原子化服务。

场景 2:on(‘scrollEvent’, ObserverOptions, callback) 带 options 监听指定 id

on 带 options 用 uiObserver.on('scrollEvent', { id: 'xxx' }, callback) 监听指定组件 id:

// ✅ 场景 2:on('scrollEvent', ObserverOptions, callback) 带 options 监听指定 id(API 11)
import uiObserver from '@ohos.arkui.observer'

uiObserver.on('scrollEvent', { id: 'scrollTarget' }, (info: uiObserver.ScrollEventInfo) => {  // ✅ ObserverOptions { id: string }
  // ✅ 只监听 id='scrollTarget' 组件的滚动事件(不传 options 监听所有组件)
  console.log(`uniqueId: ${info.uniqueId}`)  // ✅ uniqueId 是滚动组件唯一 id
})
// on + ObserverOptions:指定监听哪个组件 id 的滚动事件(不传 options 监听所有组件)

鸿蒙 on + ObserverOptions API 真名坑uiObserver.on(type: string, options: ObserverOptions, callback: AsyncCallback<ScrollEventInfo>): void 重载(带 options 指定监听组件 id);uiObserver.ObserverOptions 接口 { id: string }(id 指定监听哪个组件,string 类型);鸿蒙坑:不传 options 时监听所有组件的滚动事件,传 options 时只监听指定 id 组件——React addEventListener 监听特定 DOM 元素,鸿蒙 uiObserver.onObserverOptions.id 指定监听组件。

场景 3:off(‘scrollEvent’, callback) 取消监听——callback 传同一个引用

off 取消监听用 uiObserver.off('scrollEvent', callback),callback 必须传同一个引用:

// ✅ 场景 3:off('scrollEvent', callback) 取消监听——callback 传同一个引用(API 11)
import uiObserver from '@ohos.arkui.observer'

// ✅ 先存 callback 引用,off 时传同一个引用(不是匿名函数)
const scrollCallback: (info: uiObserver.ScrollEventInfo) => void = (info: uiObserver.ScrollEventInfo) => {
  console.log(`uniqueId: ${info.uniqueId}`)
}
uiObserver.on('scrollEvent', scrollCallback)  // ✅ on 时存 callback 引用
uiObserver.off('scrollEvent', scrollCallback)  // ✅ off 时传同一个 callback 引用

// ✅ off callback 可选(不传则取消所有 'scrollEvent' 监听)
uiObserver.off('scrollEvent')  // ✅ 不传 callback 取消所有监听
// off callback 传同一个引用:不是匿名函数,不传则取消所有监听

鸿蒙 off API 真名坑uiObserver.off(type: string, callback?: AsyncCallback<T>): void 取消监听(callback 可选);鸿蒙坑:off 的 callback 必须传同一个引用(不是匿名函数,匿名函数每次创建新引用无法匹配取消),不传 callback 则取消该 type 的所有监听——React removeEventListener 也要求传同一个 callback 引用,鸿蒙 uiObserver.off 同理。

场景 4:on(‘navDestinationUpdate’) 监听 NavDestination 路由更新

on navDestinationUpdate 用 uiObserver.on('navDestinationUpdate', callback) 监听 NavDestination 路由:

// ✅ 场景 4:on('navDestinationUpdate', callback) 监听 NavDestination 路由更新(API 11)
import uiObserver from '@ohos.arkui.observer'

uiObserver.on('navDestinationUpdate', (info: uiObserver.NavDestinationInfo) => {  // ✅ uiObserver.NavDestinationInfo
  // ✅ NavDestinationInfo 嵌套在 namespace uiObserver 里(不是顶层 export)
  // ✅ NavDestinationInfo 真属性(navigator/context/from/to——具体看 SDK 声明)
  console.log('NavDestination 路由更新触发')
})
// on navDestinationUpdate:监听 NavDestination 路由更新,NavDestinationInfo 嵌套在 namespace 里

鸿蒙 on navDestinationUpdate API 真名坑uiObserver.on('navDestinationUpdate', callback: AsyncCallback<NavDestinationInfo>): void 监听 NavDestination 路由更新;uiObserver.NavDestinationInfo 嵌套在 namespace uiObserver 里(用 uiObserver.NavDestinationInfo 命名空间访问,不是顶层 export);鸿蒙坑NavDestinationInfo 的真属性不是 React 习惯的 from/to(具体属性看 SDK 声明,直接访问 info.from/info.to 可能触发 Property does not exist 编译错——先查 SDK 真属性再用)。

五、一句话哲学

写鸿蒙 ArkUI 记住:uiObserver 不是 React addEventListener 是「namespace 顶层函数 on/off」——鸿蒙 6.1 API 23 @ohos.arkui.observer namespace(API 11+,鸿蒙 6.1 API 23 基座,uiObserver namespace default export + on/off 顶层函数 + 7 种 type + ScrollEventInfo/NavDestinationInfo 嵌套类型,SysCap SystemCapability.ArkUI.ArkUI.Full,@crossplatform @atomicservice)。根因不是 DOM 方法是 namespace 顶层函数——namespace 真名是 uiObserver 不是 observer(✅ import uiObserver fromdefault import不是 named import),❌ import { observer } from 编译错 has no exported member 'observer',❌ import { uiObserver } from 编译错(default export 不能 named import)),on(type, callback) 的 type 参数是 string literal(✅ 'scrollEvent' 不是 enum ScrollEventType,❌ 传 enum 触发 Argument of type 'ScrollEventType' is not assignable to parameter of type 'string'),ScrollEventInfo/ObserverOptions/NavDestinationInfo/NavDestinationState/ScrollEventType嵌套在 namespace uiObserver 里不是顶层 export(✅ uiObserver.ScrollEventInfo 命名空间访问,❌ import { ScrollEventInfo } 编译错 has no exported member'),ScrollEventInfo 没有 type 属性(✅ 真属性 id/uniqueId/offset/triggerOffset/observableScrollableTotalRange,❌ info.type 触发 Property 'type' does not exist 编译错,React Eventtype 鸿蒙 ScrollEventInfo 没有),on 支持 7 种 type string literal('scrollEvent'/'navDestinationUpdate'/'routerPageUpdate'/'densityUpdate'/'willDraw'/'didLayout'/'tabContentUpdate'/'navDestinationSwitch',每个对应不同 Info 类型,React 'scroll' 鸿蒙 'scrollEvent' 驼峰命名带 Event 后缀),off(type, callback) 取消监听(callback 必须传同一个引用不是匿名函数,不传则取消该 type 所有监听)。namespace 真名 uiObserver + default import + on type string literal + ScrollEventInfo 嵌套 namespace 无 type 属性是鸿蒙 6.1 arkui.observer UI 观察器坑核心!

能力系列回链

  • 鸿蒙 7.0 新特性篇 1~17(沉浸式毛玻璃/Component3D/智能体框架/方舟引擎/星盾安全/星河互联/空间音频/可变字体/游戏快启/分布式数据盾/LTPO 可变帧率/AI 文档识别/多形态服务窗口/AI 反诈/机密计算/空间计算/小艺全面进化)
  • 鸿蒙 6.1 API 23 开发坑系列篇 1「ArkUI.modifier 装饰器坑」——attributeModifier + AttributeModifier 状态化节点修改器
  • 鸿蒙 6.1 API 23 开发坑系列篇 2「arkui.componentSnapshot 组件截图坑」——get/getSync/createFromBuilder 返回 image.PixelMap 像素图
  • 鸿蒙 6.1 API 23 开发坑系列篇 3「arkui.node 节点坑」——NodeController abstract class makeNode override + BuilderNode WrappedBuilder
  • 鸿蒙 6.1 API 23 开发坑系列篇 4「arkui.UIContext UI 上下文坑」——runScopedTask 不是 runScopedOnUiThread + 11 个子管理器
  • 鸿蒙 6.1 API 23 开发坑系列篇 5「arkui.observer UI 观察器坑」——uiObserver namespace 真名不是 observer + on type string literal(本文)
Logo

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

更多推荐