HarmonyKit | 鸿蒙新特性:@Component 与 @Entry 组件的生命周期与编译约束

发布时间:2026/7/12 6:07:47
HarmonyKit | 鸿蒙新特性:@Component 与 @Entry 组件的生命周期与编译约束 HarmonyKit | 鸿蒙新特性Component 与 Entry 组件的生命周期与编译约束引言两个装饰器的分工Component和Entry是 ArkUI 最基础的两个装饰器。前者将一个 struct 标记为可复用的 UI 组件后者将它标记为可被路由系统导航到的独立页面。一个 struct 可以同时拥有两者——此时它既是一个页面入口也是一个可被其他页面嵌套的组件。在 HarmonyKit 中主页Index和 10 个工具页都是Entry——它们是独立的路由目标。ToolCard和CopyButton只是Component——它们被嵌入到页面中使用。项目仓库https://atomgit.com/VON-/harmony-kitEntry 和 Component 的区别从使用角度来看两者的核心区别在于Entry 是路由的目标Component是 UI 的构造单元。// Entry — 独立页面可以被路由导航到EntryComponentstruct JsonFormatterPage{// 页面级状态Stateinput:string;build(){// 完整的页面布局Column(){// 使用 Component 子组件CopyButton({content:this.formattedText})}}}// Component — 可复用 UI 单元Componentexportstruct CopyButton{Propcontent:string;build(){Button(复制).onClick((){// 复制逻辑})}}两者的编译约束也略有不同。Entry 组件可以访问页面级别的路由上下文onPageShow/onPageHide而 Component 不能。同时Entry 组件的样式和布局通常是完整的页面布局而 Component 只关注它负责的 UI 片段。build() 方法的严格语法限制ArkTS 的build()方法有一个核心限制只能包含 UI 组件语法。不能声明变量let、const、var不能用if/else/switch/for包裹非 UI 逻辑。以下代码在 ArkTS 中不合法// 错误build(){letresultthis.calculate();if(result0){this.doSomething();}Text(String(result))}正确做法是将逻辑迁移到build()外部// 正确getResult():string{letresultthis.calculate();returnString(result);}build(){Text(this.getResult())}这个限制在初学阶段让人沮丧——习惯了 React JSX 中能穿插任意 JS 表达式的人会觉得 ArkTS用着别扭。但这种严格性是有意为之——强制开发者将逻辑与 UI 分离。build()只负责渲染计算逻辑放在外部方法中。更正式的说法是build() 方法内部是一个UI DSL不是 TypeScript/JavaScript。UI DSL 只接受组件构造函数、装饰器调用和有限的表达式三元运算符、函数调用。一切变量声明、循环语句、条件语句都需要放在 build() 之外。// build() 中可以使用的有限表达式示例build(){Column(){// ✅ 三元运算符Text(this.isValid?有效:无效)// ✅ 函数调用Text(this.getFormattedResult())// ✅ 数组展开ForEachForEach(this.items,(item:ItemType){Text(item.label)})// ❌ 不能声明变量// ❌ 不能写 for/while 循环// ❌ 不能写 try/catch// ❌ 不能写 switch}}生命周期方法详解ArkUI 组件的生命周期方法按调用顺序排列如下aboutToAppear()组件即将显示时回调。这是初始化数据的最佳时机——在 build() 执行之前调用。aboutToAppear(){this.tsToDate();// 页面打开即刻展示当前时间戳}在 HarmonyKit 中aboutToAppear 被广泛用于预填充场景时间戳转换打开页面时自动显示当前时间戳和对应的日期UUID 生成器打开页面时自动生成一个 UUID进制转换打开页面时默认输入 255 并展示多种进制结果文本统计打开页面时展示一段示例文本的统计数据这样做的目的是零操作体验——用户打开工具页不需要先输入点什么才能看到效果页面本身就在展示有意义的内容。aboutToDisappear()组件即将销毁时回调。用于清理资源aboutToDisappear(){clearInterval(this.timer);// 清理定时器}HarmonyKit 中这个回调使用较少因为大多数工具不涉及定时器、订阅等需要手动清理的资源。如果未来加入实时相关工具如秒表、计时器aboutToDisappear 将是资源清理的关键入口。onPageShow() — 仅 Entry 可用页面每次显示时触发。注意与 aboutToAppear 的区别——aboutToAppear 只在组件首次创建时触发一次onPageShow 在每次页面出现在前台时触发包括从后台切回、从其他页面返回。onPageHide() — 仅 Entry 可用页面不再显示时触发切到后台、导航到其他页面。通常与 onPageShow 成对使用。onPageShow(){// 页面显示时刷新数据}onPageHide(){// 页面隐藏时暂停耗时操作}onBackPress() — 仅 Entry 可用用户点击系统返回键时触发。返回 true 表示拦截返回操作false 表示执行默认返回行为onBackPress():boolean{if(this.hasUnsavedChanges){this.showDiscardDialog();// 弹窗确认是否丢弃更改returntrue;// 拦截返回}returnfalse;// 允许返回}HarmonyKit 中的工具大多是无状态的每次打开都是新的输入所以没有使用 onBackPress。但对于表单类的工具例如 JWT 调试器中的编辑区域onBackPress 提供了防止数据丢失的机会。生命周期完整示例EntryComponentstruct TimestampConverter{Statetimestamp:number0;aboutToAppear(){// 1. 组件创建时设置默认值this.timestampDate.now();}onPageShow(){// 2. 页面显示时刷新数据可能从后台切回}build(){// 3. 渲染 UIColumn(){Text(this.timestamp.toString())}}onPageHide(){// 4. 页面隐藏时暂停处理}aboutToDisappear(){// 5. 组件销毁时清理资源}onBackPress():boolean{// 6. 返回键按下时returnfalse;}}页面级生命周期 vs 组件级生命周期需要特别区分的是Component 只有 aboutToAppear 和 aboutToDisappear组件级生命周期Entry 在上面两个之外还有 onPageShow、onPageHide、onBackPress页面级生命周期。区分的原因是一个页面可能由多个 Component 嵌套组成当页面在前台/后台切换时所有子组件的 aboutToAppear/aboutToDisappear 不会重复触发——触发的是页面的 onPageShow/onPageHide。只有页面真正创建和销毁时组件的 aboutToAppear 和 aboutToDisappear 才会触发。struct vs classArkUI 组件必须是struct而不能是class。struct 不支持继承——组件复用通过组合Prop/Consume而非继承实现。熟悉的 OOP 模式BasePage → JsonPage extends BasePage在 ArkUI 中不适用。// ArkUI 组件必须是 structComponentexportstruct ToolCard{Proptool:ToolItem;build(){// ...}}// 以下代码不合法// Component// export class ToolCard { ... } // 错误如果确实需要共享逻辑如多个工具页都需要 CopyButton 输出区域方案是抽出独立的 Component如 CopyButton而非抽取基类 struct。组合优于继承——听起来和 React 社区的口号一致。结构体与类在实践中还有另一个关键区别struct 不能定义自定义构造函数只能通过组件参数传递属性// ✅ 正确通过组件参数传递ToolCard({tool:item})// ❌ 错误struct 不能定义构造函数// new ToolCard(item)这个限制看似约束实际上让组件接口变得更加显式——所有输入都通过 Prop/Consume/State 声明一眼就能看出组件的依赖项。Builder 的限制Builder 方法不能拥有 State。导航映射的 NavMap 是 Builder——它只是路由表name → Page不管理状态。它只是从 name 参数查到对应的页面组件并调它的构建函数。一个常见错误HdsNavDestination 与 Entry 的关系HarmonyKit 早期尝试让工具页同时是 Entry 和 HdsNavDestination。但 HdsNavDestination 是为 HdsNavigation NavPathStack 体系设计的——它的 titleBar 配置类型和 Entry 页面的上下文不兼容。最终 HarmonyKit 选择工具页保持 Entryrouter 导航HdsTabs 用于主页底部导航栏。这个矛盾的根源在于Entry 是页面路由的旧体系HdsNavDestination 是新体系。两者不能共存于同一个页面因为它们在生命周期管理和导航上下文上有根本差异。如果后续迁移到 NavPathStack所有工具页需要从 Entry 改为普通的 Component嵌入到 HdsNavigation 的 navDestination 中。最佳实践总结Component 保持轻量一个组件只做一件事输入输出清晰避免过深的组件嵌套超过 5 层的嵌套使数据流难以追踪Entry 页面保持精简页面只做路由入口业务逻辑委托给子组件和方法合理使用生命周期aboutToAppear 做初始化aboutToDisappear 做清理组件优先于继承用组合而非继承来复用逻辑项目仓库https://atomgit.com/VON-/harmony-kit