关于 harmonyos-app
harmonyos-app 为 HarmonyOS 应用开发提供编码规范与实现参考,面向使用 ArkTS 和 ArkUI 的开发者。内容强调严格类型、Stage 模型、声明式 UI 和跨设备适配。
主要功能
- 01ArkTS 严格类型规范
- 02ArkUI 声明式界面
- 03采用 Stage 模型
- 04状态管理模式参考
- 05可复用组件设计
- 06多设备适配原则
适用场景
- 01规划 HarmonyOS 应用架构
- 02实现 ArkUI 页面与组件
- 03选择状态装饰器
- 04避免动态类型和直接修改状态
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ majiayu000/ spellbook --skill harmonyos-app安装后在 Claude Code 中输入/harmonyos-app确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
reference/arkts.mdarkui.mddistributed.mdextended.mdstage-model.mdtemplates/project-template.md详细介绍
HarmonyOS 应用开发
核心原则
- ArkTS 优先 — 使用 ArkTS,并确保严格的类型安全,不使用
any或动态类型 - 声明式 UI — 使用 ArkUI 的声明式组件和状态管理来构建 UI
- Stage 模型 — 使用现代 Stage 模型(UIAbility),而非旧版 FA 模型
- 面向分布式设计 — 从一开始就利用跨设备能力
- 原子化服务 — 考虑使用原子化服务和卡片,打造轻量体验
- 一次开发 — 设计时考虑多设备适配(手机、平板、手表、电视)
硬性规则(必须遵守)
这些规则是强制要求。违反这些规则意味着该技能未能正常工作。
禁止动态类型
ArkTS 禁止动态类型。绝不要使用 any、类型断言或动态属性访问。
// ❌ FORBIDDEN: Dynamic types
let data: any = fetchData();
let obj: object = {};
obj['dynamicKey'] = value; // Dynamic property access
(someVar as SomeType).method(); // Type assertion
// ✅ REQUIRED: Strict typing
interface UserData {
id: string;
name: string;
}
let data: UserData = fetchData();
// Use Record for dynamic keys
let obj: Record<string, string> = {};
obj['key'] = value; // OK with Record type
禁止直接修改状态
绝不要直接修改嵌套对象中的 @State/@Prop 变量。请使用不可变更新。
// ❌ FORBIDDEN: Direct mutation
@State user: User = { name: 'John', age: 25 };
updateAge() {
this.user.age = 26; // UI won't update!
}
// ✅ REQUIRED: Immutable update
updateAge() {
this.user = { ...this.user, age: 26 }; // Creates new object, triggers UI update
}
// For arrays
@State items: string[] = ['a', 'b'];
// ❌ FORBIDDEN
this.items.push('c'); // UI won't update
// ✅ REQUIRED
this.items = [...this.items, 'c'];
仅使用 Stage 模型
始终使用 Stage 模型(UIAbility)。绝不要使用已弃用的 FA 模型(PageAbility)。
// ❌ FORBIDDEN: FA Model (deprecated)
// config.json with "pages" array
export default {
onCreate() { ... } // PageAbility lifecycle
}
// ✅ REQUIRED: Stage Model
// module.json5 with abilities configuration
import { UIAbility } from '@kit.AbilityKit';
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
// Modern Stage model lifecycle
}
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.loadContent('pages/Index');
}
}
组件可复用性
将可复用 UI 提取到 @Component 中。不要在 build() 方法中内联复杂 UI。
// ❌ FORBIDDEN: Monolithic build method
@Entry
@Component
struct MainPage {
build() {
Column() {
// 200+ lines of inline UI...
Row() {
Image($r('app.media.avatar'))
Column() {
Text(this.user.name)
Text(this.user.email)
}
}
// More inline UI...
}
}
}
// ✅ REQUIRED: Extract components
@Component
struct UserCard {
@Prop user: User;
build() {
Row() {
Image($r('app.media.avatar'))
Column() {
Text(this.user.name)
Text(this.user.email)
}
}
}
}
@Entry
@Component
struct MainPage {
@State user: User = { name: 'John', email: '[email protected]' };
build() {
Column() {
UserCard({ user: this.user })
}
}
}
快速参考
何时使用何种方式
| 场景 | 模式 | 示例 |
|---|---|---|
| 组件局部状态 | @State | 计数器、表单输入 |
| 父组件到子组件的数据传递 | @Prop | 只读子组件数据 |
| 双向绑定 | @Link | 共享可变状态 |
| 跨组件状态 | @Provide/@Consume | 主题、用户上下文 |
| 持久化状态 | PersistentStorage | 用户偏好设置 |
| 全应用状态 | AppStorage | 全局状态 |
| 复杂状态逻辑 | @Observed/@ObjectLink | 嵌套对象更新 |
状态装饰器选择
@State → Component owns the state, triggers re-render on change
@Prop → Parent passes value, child gets copy (one-way)
@Link → Parent passes reference, child can modify (two-way)
@Provide → Ancestor provides value to all descendants
@Consume → Descendant consumes value from ancestor
@StorageLink → Syncs with AppStorage, two-way binding
@StorageProp → Syncs with AppStorage, one-way binding
@Observed → Class decorator for observable objects
@ObjectLink → Links to @Observed object in parent
项目结构
推荐架构
MyApp/
├── entry/ # Main entry module
│ ├── src/main/
│ │ ├── ets/
│ │ │ ├── entryability/ # UIAbility definitions
│ │ │ │ └── EntryAbility.ets
│ │ │ ├── pages/ # Page components
│ │ │ │ ├── Index.ets
│ │ │ │ └── Detail.ets
│ │ │ ├── components/ # Reusable UI components
│ │ │ │ ├── common/ # Common components
│ │ │ │ └── business/ # Business-specific components
│ │ │ ├── viewmodel/ # ViewModels (MVVM)
│ │ │ ├── model/ # Data models
│ │ │ ├── service/ # Business logic services
│ │ │ ├── repository/ # Data access layer
│ │ │ ├── utils/ # Utility functions
│ │ │ └── constants/ # Constants and configs
│ │ ├── resources/ # Resources (strings, images)
│ │ └── module.json5 # Module configuration
│ └── build-profile.json5
├── common/ # Shared library module
│ └── src/main/ets/
├── features/ # Feature modules
│ ├── feature_home/
│ └── feature_profile/
└── build-profile.json5 # Project configuration
分层
┌─────────────────────────────────────┐
│ UI Layer (Pages) │ ArkUI Components
├─────────────────────────────────────┤
│ ViewModel Layer │ State management, UI logic
├─────────────────────────────────────┤
│ Service Layer │ Business logic
├─────────────────────────────────────┤
│ Repository Layer │ Data access abstraction
├─────────────────────────────────────┤
│ Data Sources (Local/Remote) │ Preferences, RDB, Network
└─────────────────────────────────────┘
ArkUI 组件模式
基本组件结构
import { router } from '@kit.ArkUI';
@Component
export struct ProductCard {
// Props from parent
@Prop product: Product;
@Prop onAddToCart: (product: Product) => void;
// Local state
@State isExpanded: boolean = false;
// Computed values (use getters)
get formattedPrice(): string {
return `¥${this.product.price.toFixed(2)}`;
}
// Lifecycle
aboutToAppear(): void {
console.info('ProductCard appearing');
}
aboutToDisappear(): void {
console.info('ProductCard disappearing');
}
// Event handlers
private handleTap(): void {
router.pushUrl({ url: 'pages/ProductDetail', params: { id: this.product.id } });
}
private handleAddToCart(): void {
this.onAddToCart(this.product);
}
// UI builder
build() {
Column() {
Image(this.product.imageUrl)
.width('100%')
.aspectRatio(1)
.objectFit(ImageFit.Cover)
Text(this.product.name)
.fontSize(16)
.fontWeight(FontWeight.Medium)
Text(this.formattedPrice)
.fontSize(14)
.fontColor('#FF6B00')
Button('Add to Cart')
.onClick(() => this.handleAddToCart())
}
.padding(12)
.backgroundColor(Color.White)
.borderRadius(8)
.onClick(() => this.handleTap())
}
}
使用 LazyForEach 的列表
import { BasicDataSource } from '../utils/BasicDataSource';
class ProductDataSource extends BasicDataSource<Product> {
private products: Product[] = [];
totalCount(): number {
return this.products.length;
}
getData(index: number): Product {
return this.products[index];
}
addData(product: Product): void {
this.products.push(product);
this.notifyDataAdd(this.products.length - 1);
}
updateData(index: number, product: Product): void {
this.products[index] = product;
this.notifyDataChange(index);
}
}
@Component
struct ProductList {
private dataSource: ProductDataSource = new ProductDataSource();
build() {
List() {
LazyForEach(this.dataSource, (product: Product, index: number) => {
ListItem() {
ProductCard({ product: product })
}
}, (product: Product) => product.id) // Key generator
}
.lanes(2) // Grid with 2 columns
.cachedCount(4) // Cache 4 items for smooth scrolling
}
}
自定义对话框
@CustomDialog
struct ConfirmDialog {
controller: CustomDialogController;
title: string = 'Confirm';
message: string = '';
onConfirm: () => void = () => {};
build() {
Column() {
Text(this.title)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 16 })
Text(this.message)
.fontSize(16)
.margin({ bottom: 24 })
Row() {
Button('Cancel')
.onClick(() => this.controller.close())
.backgroundColor(Color.Gray)
.margin({ right: 16 })
Button('Confirm')
.onClick(() => {
this.onConfirm();
this.controller.close();
})
}
}
.padding(24)
}
}
// Usage
@Entry
@Component
struct MainPage {
dialogController: CustomDialogController = new CustomDialogController({
builder: ConfirmDialog({
title: 'Delete Item',
message: 'Are you sure you want to delete this item?',
onConfirm: () => this.deleteItem()
}),
autoCancel: true
});
private deleteItem(): void {
// Delete logic
}
build() {
Button('Delete')
.onClick(() => this.dialogController.open())
}
}
扩展参考
从 ## State Management Patterns 开始的详细资料已移至 reference/extended.md,以使此技能内容精简。如果任务需要已移出的示例、命令目录、检查清单、平台详情或实现模板,请加载该参考资料。
评论
支持 Markdown