Harmonyos App

majiayu000/spellbook/skills/harmonyos-app

作者 majiayu000ed52af74bce85ba9e71db109c72708833012eaf2無授權條款收錄於 2026年10月9日更新於 2026年10月9日

HarmonyOS application development expert. Use when building HarmonyOS apps with ArkTS, ArkUI, Stage model, and distributed capabilities. Covers HarmonyOS NEXT (API 12+) best practices.

AI 產生的概覽

指導使用 ArkTS、ArkUI、Stage 模型與分散式能力進行 HarmonyOS 應用程式開發。

功能
此技能為建構 HarmonyOS 應用程式提供專業指引,涵蓋 ArkTS、ArkUI 宣告式 UI、Stage 模型與分散式功能。它提供強制編碼規則、狀態管理模式、專案結構建議與元件範例,並附上 ArkTS、ArkUI、分散式能力、Stage 模型及延伸內容的參考檔案。此外還包含一份專案範本文件。
適用情境
適用於開發或組織 HarmonyOS 應用程式,尤其是 HarmonyOS NEXT(API 12+)。適合涉及 ArkTS 型別安全、ArkUI 元件與狀態裝飾器、UIAbility Stage 模型建置或跨裝置分散式設計的工作。
執行需求
無需指令碼或特殊工具,僅包含說明與參考文件。代理需要能夠存取隨附的參考檔案與範本檔案,以載入詳細內容。

HarmonyOS Application Development

Core Principles

  • ArkTS First — Use ArkTS with strict type safety, no any or dynamic types
  • Declarative UI — Build UI with ArkUI's declarative components and state management
  • Stage Model — Use modern Stage model (UIAbility), not legacy FA model
  • Distributed by Design — Leverage cross-device capabilities from the start
  • Atomic Services — Consider atomic services and cards for lightweight experiences
  • One-time Development — Design for multi-device adaptation (phone, tablet, watch, TV)

Hard Rules (Must Follow)

These rules are mandatory. Violating them means the skill is not working correctly.

No Dynamic Types

ArkTS prohibits dynamic typing. Never use any, type assertions, or dynamic property access.

typescript
// ❌ FORBIDDEN: Dynamic typeslet data: any = fetchData();let obj: object = {};obj['dynamicKey'] = value;  // Dynamic property access(someVar as SomeType).method();  // Type assertion
// ✅ REQUIRED: Strict typinginterface UserData {  id: string;  name: string;}let data: UserData = fetchData();
// Use Record for dynamic keyslet obj: Record<string, string> = {};obj['key'] = value;  // OK with Record type

No Direct State Mutation

Never mutate @State/@Prop variables directly in nested objects. Use immutable updates.

typescript
// ❌ FORBIDDEN: Direct mutation@State user: User = { name: 'John', age: 25 };
updateAge() {  this.user.age = 26;  // UI won't update!}
// ✅ REQUIRED: Immutable updateupdateAge() {  this.user = { ...this.user, age: 26 };  // Creates new object, triggers UI update}
// For arrays@State items: string[] = ['a', 'b'];
// ❌ FORBIDDENthis.items.push('c');  // UI won't update
// ✅ REQUIREDthis.items = [...this.items, 'c'];

Stage Model Only

Always use Stage model (UIAbility). Never use deprecated FA model (PageAbility).

typescript
// ❌ FORBIDDEN: FA Model (deprecated)// config.json with "pages" arrayexport default {  onCreate() { ... }  // PageAbility lifecycle}
// ✅ REQUIRED: Stage Model// module.json5 with abilities configurationimport { 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');  }}

Component Reusability

Extract reusable UI into @Component. No inline complex UI in build() methods.

typescript
// ❌ FORBIDDEN: Monolithic build method@Entry@Componentstruct 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@Componentstruct UserCard {  @Prop user: User;
  build() {    Row() {      Image($r('app.media.avatar'))      Column() {        Text(this.user.name)        Text(this.user.email)      }    }  }}
@Entry@Componentstruct MainPage {  @State user: User = { name: 'John', email: '[email protected]' };
  build() {    Column() {      UserCard({ user: this.user })    }  }}

Quick Reference

When to Use What

ScenarioPatternExample
Component-local state@StateCounter, form inputs
Parent-to-child data@PropRead-only child data
Two-way binding@LinkShared mutable state
Cross-component state@Provide/@ConsumeTheme, user context
Persistent statePersistentStorageUser preferences
App-wide stateAppStorageGlobal state
Complex state logic@Observed/@ObjectLinkNested object updates

State Decorator Selection

@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

Project Structure

Recommended Architecture

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

Layer Separation

┌─────────────────────────────────────┐│           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 Component Patterns

Basic Component Structure

typescript
import { router } from '@kit.ArkUI';
@Componentexport 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())  }}

List with LazyForEach

typescript
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);  }}
@Componentstruct 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  }}

Custom Dialog

typescript
@CustomDialogstruct 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@Componentstruct 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())  }}

Extended Reference

Detailed material starting at ## State Management Patterns has been moved to reference/extended.md [blocked] to keep this skill concise. Load that reference when the task requires the moved examples, command catalogs, checklists, platform details, or implementation templates.

來源與署名

來源:majiayu000/spellbook位於skills/harmonyos-app提交ed52af7

授權條款: 無授權條款

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架