HarmonyOSアプリ開発|ArkTS/ArkUI実装ガイド
HarmonyOSアプリ開発の実装ガイド。ArkTS・ArkUI・Stageモデル・分散機能を使った構築手順を、HarmonyOS NEXT(API 12以降)のベストプラクティスとともに解説。
description の原文を見る
HarmonyOS application development expert. Use when building HarmonyOS apps with ArkTS, ArkUI, Stage model, and distributed capabilities. Covers HarmonyOS NEXT (API 12+) best practices.
SKILL.md 本文
HarmonyOS Application Development
Core Principles
- ArkTS First — Use ArkTS with strict type safety, no
anyor 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.
// ❌ 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
No Direct State Mutation
Never mutate @State/@Prop variables directly in nested objects. Use immutable updates.
// ❌ 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 Model Only
Always use Stage model (UIAbility). Never use deprecated FA model (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');
}
}
Component Reusability
Extract reusable UI into @Component. No inline complex UI in build() methods.
// ❌ 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: 'john@example.com' };
build() {
Column() {
UserCard({ user: this.user })
}
}
}
Quick Reference
When to Use What
| Scenario | Pattern | Example |
|---|---|---|
| Component-local state | @State | Counter, form inputs |
| Parent-to-child data | @Prop | Read-only child data |
| Two-way binding | @Link | Shared mutable state |
| Cross-component state | @Provide/@Consume | Theme, user context |
| Persistent state | PersistentStorage | User preferences |
| App-wide state | AppStorage | Global state |
| Complex state logic | @Observed/@ObjectLink | Nested 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
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())
}
}
List with 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
}
}
Custom Dialog
@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())
}
}
Extended Reference
Detailed material starting at ## State Management Patterns has been moved to to keep this skill concise. Load that reference when the task requires the moved examples, command catalogs, checklists, platform details, or implementation templates.reference/extended.md
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- majiayu000
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/majiayu000/claude-arsenal / ライセンス: MIT
関連スキル
agent-browser
AI エージェント向けのブラウザ自動化 CLI です。ウェブサイトとの対話が必要な場合に使用します。ページ遷移、フォーム入力、ボタンクリック、スクリーンショット取得、データ抽出、ウェブアプリのテスト、ブラウザ操作の自動化など、あらゆるブラウザタスクに対応できます。「ウェブサイトを開く」「フォームに記入する」「ボタンをクリックする」「スクリーンショットを取得する」「ページからデータを抽出する」「このウェブアプリをテストする」「サイトにログインする」「ブラウザ操作を自動化する」といった要求や、プログラマティックなウェブ操作が必要なタスクで起動します。
anyskill
AnySkill — あなたのプライベート・スキルクラウド。GitHubを基盤としたリポジトリからエージェントスキルを管理、同期、動的にロードできます。自然言語でクラウドスキルを検索し、オンデマンドでプロンプトを自動ロード、カスタムスキルのアップロードと共有、スキルバンドルの一括インストールが可能です。OpenClaw、Antigravity、Claude Code、Cursorに対応しています。
engram
AIエージェント向けの永続的なメモリシステムです。バグ修正、意思決定、発見、設定変更の後はmem_saveを使用してください。ユーザーが「覚えている」「記憶している」と言及した場合、または以前のセッションと重複する作業を開始する際はmem_searchを使用します。セッション終了前にmem_session_summaryを使用して、コンテキストを保持してください。
skyvern
AI駆動のブラウザ自動化により、任意のウェブサイトを自動化できます。フォーム入力、データ抽出、ファイルダウンロード、ログイン、複数ステップのワークフロー実行など、ユーザーがウェブサイトと連携する必要があるときに使用します。Skyvernは、LLMとコンピュータビジョンを活用して、未知のサイトも自動操作可能です。Python SDK、TypeScript SDK、REST API、MCPサーバー、またはCLIを通じて統合できます。
pinchbench
PinchBenchベンチマークを実行して、OpenClawエージェントの実世界タスクにおけるパフォーマンスを評価できます。モデルの機能テスト、モデル間の比較、ベンチマーク結果のリーダーボード提出、またはOpenClawのセットアップがカレンダー、メール、リサーチ、コーディング、複数ステップのワークフローにどの程度対応しているかを確認する際に使用します。
openui
OpenUIとOpenUI Langを使用してジェネレーティブUIアプリを構築できます。これらはLLM生成インターフェースのためのトークン効率的なオープン標準です。OpenUI、@openuidev、ジェネレーティブUI、LLMからのストリーミングUI、AI向けコンポーネントライブラリ、またはjson-render/A2UIの置き換えについて述べる際に使用します。スキャフォルディング、defineComponent、システムプロンプト、Renderer、およびOpenUI Lang出力のデバッグに対応しています。