一、现象:同一个坑,项目里踩了五次

这个项目做了十几个组件,但「保留属性名冲突」这个坑,在至少五个地方留下了注释痕迹:

// FlowAvatar.ets
/** Logical pixel size. Named avatarSize because `size` is an ArkUI attribute. */
@Prop avatarSize: number = 64;
/** Depth shadow. Named showShadow because `shadow` is an ArkUI attribute. */
@Prop showShadow: boolean = true;

// ThinkingOrb.ets
/** Logical preset size. Named orbSize because `size` is an ArkUI attribute. */
@Prop orbSize: ThinkingOrbSize = ThinkingOrbSize.Avatar;

// BorderBeam.ets
/** Corner radius in vp. Named beamRadius because borderRadius is an ArkUI attribute. */
@Prop beamRadius: number = -1;
/** Glow brightness multiplier. Named beamBrightness because brightness is an ArkUI attribute. */
@Prop beamBrightness: number = -1;

还有 GrokBot 的 botSize(不用 size)、SlotText 的 rollDirection(不用 direction)。

关键问题来了:为什么「给 @Prop 起名 size」会报错?这背后是 ArkUI 一个非常具体的机制,搞清楚它,你就再也不会踩。


二、根因:@Prop 名字和组件属性是同名冲突

2.1 机制

在 ArkUI 里,@Component@Prop 成员,本质上会变成这个组件的构造参数 / 属性。而当你在 build() 里这样写:

build() {
  Column()
    .size({ width: 100, height: 100 })   // ← 通用属性 size
}

ArkUI 的「通用属性」size全局保留的 attribute 名。如果你的组件同时声明了:

@Prop size: number = 64;

就会产生命名冲突——编译器无法区分「这个 size 是我的 @Prop,还是 ArkUI 内置的属性」。

2.2 为什么有时候「不报错但错乱」

更危险的是部分情况不报错,但语义错乱。比如 shadowdirectionborderRadius 这些,如果只在特定上下文里冲突,编译器可能不会立刻拒绝,但你的「自定义 @Prop shadow」会和「通用阴影属性」混在一起,导致传参不生效、或者样式被意外覆盖。

这就是为什么「保留属性名」是比「保留关键字」更隐蔽的坑——它不一定报错,可能只是让你的组件行为悄悄变错。


三、高危保留名清单

基于项目里实际踩过的 + ArkUI 通用属性,整理一份「起名要避开的高危名单」:

想表达的含义

❌ 高危名

✅ 项目里的替代

尺寸

size

avatarSize / orbSize / botSize

阴影

shadow

showShadow

方向

direction

rollDirection / axis

圆角

borderRadius

beamRadius / cornerRadius

亮度

brightness

beamBrightness / glow

透明度

opacity

dim / alpha

缩放

scale

visualScale / zoom

模糊

blur

sideBlur / blurRadius

通用规律:所有 ArkUI 通用属性sizeopacityscalerotateblurshadowborderRadiusbackgroundColorpaddingmargin ……)以及布局方向属性directionalign),都不该拿来给 @Prop 命名。


四、为什么「加个前缀」就够了

项目里所有的替代名都遵循同一个规律:加一个「语义前缀」,把「通用词」变成「专有词」。

原意

前缀法

size

avatarSizeorbSizebotSizecellSize

shadow

showShadow

radius

beamRadiuscardRadiuscellRadius

brightness

beamBrightness

前缀的作用是消除二义性avatarSize 明确是「这个头像组件的尺寸」,不会和 size 通用属性混淆。

但注意:前缀不能是随意的,要表达「这个组件特有的语义」avatarSize 读起来是「头像的尺寸」,orbSize 是「状态球的尺寸」,botSize 是「机器人尺寸」——每个都落在「组件自身」的语义里,而不是泛泛的 size


五、一个容易忽略的「边界 case」:label

项目里 GrokBot 和 GradientSpin 都用了 @Prop label

// GrokBot.ets
@Prop label: string = 'GrokBot';
// GradientSpin.ets
@Prop label: string = 'Loading';

label 严格来说也是 ArkUI 的通用属性(TextButton 等都有 label)。但这里它们没踩坑,原因是:

  • 这些 @Prop label自定义组件的成员,不是直接作为 ArkUI 内置属性的替代;

  • 组件内部是用 .accessibilityText(this.label) 把值消费掉,而不是 @Prop label 和内置 label 在同一层冲突。

这提醒我们:「保留名」的判断要结合「具体冲突场景」label 在这些组件里之所以安全,是因为它没有被用来「和内置 label 抢同一个语义槽」。但如果你在 build() 里同时写 .label(this.label) 和声明 @Prop label,就要小心了——能避开就避开,用 accessibilityLabeltitle 更稳妥(SlotText 就用了 accessibilityLabel)。


六、落地成一条命名纪律

把坑沉淀成习惯,三条就够:

  1. @Prop 名字永远别和 ArkUI 通用属性撞——起名前列一下「我要表达的概念」是否在 size/opacity/scale/rotate/blur/shadow/direction/borderRadius/... 这个清单里。

  2. 撞了就加「组件语义前缀」——avatarSizeorbSizebeamRadius,而不是 mySizesize2 这种无意义前缀。

  3. 注释里写明白「为什么叫这个名」——项目里每个改名的 @Prop 上面都留了 Named X because Y is an ArkUI attribute,这不是多余,而是给下一个维护者(和未来的自己)的提示,避免有人「好心」把它改回 size 又踩一遍。


七、总结

「保留属性名」这个坑,单独看很小,但它有三个讨厌的特质:

  • 高频:几乎每个自定义组件都会碰到「尺寸/阴影/方向/圆角」这些概念。

  • 隐蔽:不一定报错,可能只是行为悄悄错乱。

  • 会复发:这次记住了 size,下次换个组件又踩 direction

所以它值得被单独拎出来写一篇——不是因为它复杂,而是因为它最好的解法是一份「起名 blacklist」+ 一个「加前缀」的习惯,提前防住,而不是每次报错了再改

@Prop 别叫 size——叫 avatarSizeorbSize
撞了 ArkUI 通用属性,加组件语义前缀。
改了名就留注释,别让下一个人改回去再踩一遍。

这一篇,加上前面的《分层范式》《DisplaySync》《移植方法论》三篇,组成了 ArkUILab 项目在鸿蒙自定义组件开发上的完整横切方法论:怎么分层、怎么驱动帧、怎么移植、怎么命名。四篇合起来,就是一个「从零写一个可复用、可测试、可移植的鸿蒙动效组件」的完整地图。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐