mcpField
H
harmonyos-app

harmonyos-app

github.com/majiayu000开发288491 次安装今天更新
代码仓库

关于 harmonyos-app

harmonyos-app 为 HarmonyOS 应用开发提供编码规范与实现参考,面向使用 ArkTS 和 ArkUI 的开发者。内容强调严格类型、Stage 模型、声明式 UI 和跨设备适配。

主要功能

  1. 01ArkTS 严格类型规范
  2. 02ArkUI 声明式界面
  3. 03采用 Stage 模型
  4. 04状态管理模式参考
  5. 05可复用组件设计
  6. 06多设备适配原则

适用场景

  1. 01规划 HarmonyOS 应用架构
  2. 02实现 ArkUI 页面与组件
  3. 03选择状态装饰器
  4. 04避免动态类型和直接修改状态

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/majiayu000/spellbook --skill harmonyos-app

安装后在 Claude Code 中输入/harmonyos-app确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

reference/
arkts.md
arkui.md
distributed.md
extended.md
stage-model.md
templates/
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