fix: move skill directories to repo root and update AGENTS.md structure diagram

This commit is contained in:
cheliangzhao
2026-02-10 20:04:27 +08:00
parent 2378b310a5
commit a984e27246
13 changed files with 113 additions and 145 deletions

292
arkts-development/SKILL.md Normal file
View File

@@ -0,0 +1,292 @@
---
name: arkts-development
description: HarmonyOS ArkTS application development with ArkUI declarative UI framework. Use when building HarmonyOS/OpenHarmony apps, creating ArkUI components, implementing state management with decorators (@State, @Prop, @Link), migrating from TypeScript to ArkTS, or working with HarmonyOS-specific APIs (router, http, preferences). Covers component lifecycle, layout patterns, and ArkTS language constraints.
---
# ArkTS Development
Build HarmonyOS applications using ArkTS and the ArkUI declarative UI framework.
## Quick Start
Create a basic component:
```typescript
@Entry
@Component
struct HelloWorld {
@State message: string = 'Hello, ArkTS!';
build() {
Column() {
Text(this.message)
.fontSize(30)
.fontWeight(FontWeight.Bold)
Button('Click Me')
.onClick(() => { this.message = 'Button Clicked!'; })
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
```
## State Management Decorators
| Decorator | Usage | Description |
|-----------|-------|-------------|
| `@State` | `@State count: number = 0` | Component internal state |
| `@Prop` | `@Prop title: string` | Parent → Child (one-way) |
| `@Link` | `@Link value: number` | Parent ↔ Child (two-way, use `$varName`) |
| `@Provide/@Consume` | Cross-level | Ancestor → Descendant |
| `@Observed/@ObjectLink` | Nested objects | Deep object observation |
## Common Layouts
```typescript
// Vertical
Column({ space: 10 }) { Text('A'); Text('B'); }
.alignItems(HorizontalAlign.Center)
// Horizontal
Row({ space: 10 }) { Text('A'); Text('B'); }
.justifyContent(FlexAlign.SpaceBetween)
// Stack (overlay)
Stack({ alignContent: Alignment.Center }) {
Image($r('app.media.bg'))
Text('Overlay')
}
// List with ForEach
List({ space: 10 }) {
ForEach(this.items, (item: string) => {
ListItem() { Text(item) }
}, (item: string) => item)
}
```
## Component Lifecycle
```typescript
@Entry
@Component
struct Page {
aboutToAppear() { /* Init data */ }
onPageShow() { /* Page visible */ }
onPageHide() { /* Page hidden */ }
aboutToDisappear() { /* Cleanup */ }
build() { Column() { Text('Page') } }
}
```
## Navigation
```typescript
import { router } from '@kit.ArkUI';
// Push
router.pushUrl({ url: 'pages/Detail', params: { id: 123 } });
// Replace
router.replaceUrl({ url: 'pages/New' });
// Back
router.back();
// Get params
const params = router.getParams() as Record<string, Object>;
```
## Network Request
```typescript
import { http } from '@kit.NetworkKit';
const req = http.createHttp();
const res = await req.request('https://api.example.com/data', {
method: http.RequestMethod.GET,
header: { 'Content-Type': 'application/json' }
});
if (res.responseCode === 200) {
const data = JSON.parse(res.result as string);
}
req.destroy();
```
## Local Storage
```typescript
import { preferences } from '@kit.ArkData';
const prefs = await preferences.getPreferences(this.context, 'store');
await prefs.put('key', 'value');
await prefs.flush();
const val = await prefs.get('key', 'default');
```
## ArkTS Language Constraints
ArkTS enforces stricter rules than TypeScript for performance and safety:
| Prohibited | Use Instead |
|------------|-------------|
| `any`, `unknown` | Explicit types, interfaces |
| `var` | `let`, `const` |
| Dynamic property access `obj['key']` | Fixed object structure |
| `for...in`, `delete`, `with` | `for...of`, array methods |
| `#privateField` | `private` keyword |
| Structural typing | Explicit `implements`/`extends` |
See [references/migration-guide.md](references/migration-guide.md) for complete TypeScript → ArkTS migration details.
## Command Line Build (hvigorw)
hvigorw is the Hvigor wrapper tool for command-line builds.
```bash
# Common build tasks
hvigorw clean # Clean build directory
hvigorw assembleHap -p buildMode=debug # Build Hap (debug)
hvigorw assembleApp -p buildMode=release # Build App (release)
hvigorw assembleHar # Build Har library
hvigorw assembleHsp # Build Hsp
# Build specific module
hvigorw assembleHap -p module=entry@default --mode module
# Run tests
hvigorw onDeviceTest -p module=entry -p coverage=true
hvigorw test -p module=entry # Local test
# CI/CD recommended
hvigorw assembleApp -p buildMode=release --no-daemon
```
Common parameters:
| Parameter | Description |
|-----------|-------------|
| `-p buildMode={debug\|release}` | Build mode |
| `-p product={name}` | Target product (default: default) |
| `-p module={name}@{target}` | Target module (with `--mode module`) |
| `--no-daemon` | Disable daemon (recommended for CI) |
| `--analyze=advanced` | Enable build analysis |
| `--optimization-strategy=memory` | Memory-optimized build |
See [references/hvigor-commandline.md](references/hvigor-commandline.md) for complete command reference.
## Code Linter (codelinter)
codelinter is the code checking and fixing tool for ArkTS/TS files.
```bash
# Basic usage
codelinter # Check current project
codelinter /path/to/project # Check specified project
codelinter -c ./code-linter.json5 # Use custom rules
# Check and auto-fix
codelinter --fix
codelinter -c ./code-linter.json5 --fix
# Output formats
codelinter -f json -o ./report.json # JSON report
codelinter -f html -o ./report.html # HTML report
# Incremental check (Git changes only)
codelinter -i
# CI/CD with exit codes
codelinter --exit-on error,warn # Non-zero exit on error/warn
```
| Parameter | Description |
|-----------|-------------|
| `-c, --config <file>` | Specify rules config file |
| `--fix` | Auto-fix supported issues |
| `-f, --format` | Output format: default/json/xml/html |
| `-o, --output <file>` | Save result to file |
| `-i, --incremental` | Check only Git changed files |
| `-p, --product <name>` | Specify product |
| `-e, --exit-on <levels>` | Exit code levels: error,warn,suggestion |
See [references/codelinter.md](references/codelinter.md) for complete reference.
## Stack Trace Parser (hstack)
hstack parses obfuscated crash stacks from Release builds back to source code locations.
```bash
# Parse crash files directory
hstack -i crashDir -o outputDir -s sourcemapDir -n nameCacheDir
# Parse with C++ symbols
hstack -i crashDir -o outputDir -s sourcemapDir --so soDir -n nameCacheDir
# Parse single crash stack
hstack -c "at func (entry|entry|1.0.0|src/main/ets/pages/Index.ts:58:58)" -s sourcemapDir
```
| Parameter | Description |
|-----------|-------------|
| `-i, --input` | Crash files directory |
| `-c, --crash` | Single crash stack string |
| `-o, --output` | Output directory (or file with `-c`) |
| `-s, --sourcemapDir` | Sourcemap files directory |
| `--so, --soDir` | Shared object (.so) files directory |
| `-n, --nameObfuscation` | NameCache files directory |
Requirements:
- Must provide either `-i` or `-c` (not both)
- Must provide at least `-s` or `--so`
- For method name restoration, provide both `-s` and `-n`
See [references/hstack.md](references/hstack.md) for complete reference.
## Code Obfuscation (ArkGuard)
Enable in `build-profile.json5`:
```json
"arkOptions": {
"obfuscation": {
"ruleOptions": {
"enable": true,
"files": ["./obfuscation-rules.txt"]
}
}
}
```
Common rules in `obfuscation-rules.txt`:
```text
-enable-property-obfuscation # Property name obfuscation
-enable-toplevel-obfuscation # Top-level scope obfuscation
-enable-filename-obfuscation # Filename obfuscation
-keep-property-name apiKey # Whitelist specific names
```
See [references/arkguard-obfuscation.md](references/arkguard-obfuscation.md) for complete guide.
## Reference Files
- **Migration Guide**: [references/migration-guide.md](references/migration-guide.md) - Complete TypeScript to ArkTS migration rules and examples
- **Component Patterns**: [references/component-patterns.md](references/component-patterns.md) - Advanced component patterns and best practices
- **API Reference**: [references/api-reference.md](references/api-reference.md) - Common HarmonyOS APIs
- **ArkGuard Obfuscation**: [references/arkguard-obfuscation.md](references/arkguard-obfuscation.md) - Code obfuscation configuration and troubleshooting
- **Hvigor Command Line**: [references/hvigor-commandline.md](references/hvigor-commandline.md) - Complete hvigorw build tool reference
- **CodeLinter**: [references/codelinter.md](references/codelinter.md) - Code checking and fixing tool
- **Hstack**: [references/hstack.md](references/hstack.md) - Crash stack trace parser for Release builds
## Development Environment
- **IDE**: DevEco Studio
- **SDK**: HarmonyOS SDK
- **Simulator**: Built-in DevEco Studio emulator
## Related Skills
- **Build & Deploy**: See `harmonyos-build-deploy` skill for building, packaging, and device installation

View File

@@ -0,0 +1,57 @@
// ArkTS Basic Component Template
// Replace {{ComponentName}} with your component name
@Entry
@Component
struct {{ComponentName}} {
// State management
@State isLoading: boolean = false;
@State errorMessage: string = '';
// Lifecycle
aboutToAppear(): void {
this.loadData();
}
aboutToDisappear(): void {
// Cleanup resources
}
// Methods
async loadData(): Promise<void> {
this.isLoading = true;
try {
// Load data here
} catch (error) {
this.errorMessage = 'Failed to load data';
} finally {
this.isLoading = false;
}
}
// Build
build() {
Column() {
if (this.isLoading) {
LoadingProgress()
.width(50)
.height(50)
} else if (this.errorMessage) {
Text(this.errorMessage)
.fontSize(16)
.fontColor(Color.Red)
Button('Retry')
.onClick(() => { this.loadData(); })
} else {
// Main content here
Text('{{ComponentName}}')
.fontSize(24)
.fontWeight(FontWeight.Bold)
}
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
}
}

View File

@@ -0,0 +1,129 @@
// ArkTS List Page Template
// Replace {{PageName}}, {{ItemType}}, {{itemName}} with your values
import { router } from '@kit.ArkUI';
interface {{ItemType}} {
id: string;
title: string;
description: string;
}
@Entry
@Component
struct {{PageName}} {
@State items: {{ItemType}}[] = [];
@State isLoading: boolean = false;
@State isRefreshing: boolean = false;
aboutToAppear(): void {
this.loadItems();
}
async loadItems(): Promise<void> {
this.isLoading = true;
try {
// TODO: Fetch items from API
this.items = [
{ id: '1', title: 'Item 1', description: 'Description 1' },
{ id: '2', title: 'Item 2', description: 'Description 2' },
];
} finally {
this.isLoading = false;
}
}
async refreshItems(): Promise<void> {
this.isRefreshing = true;
await this.loadItems();
this.isRefreshing = false;
}
navigateToDetail(item: {{ItemType}}): void {
router.pushUrl({
url: 'pages/{{PageName}}Detail',
params: { id: item.id }
});
}
@Builder
ItemCard(item: {{ItemType}}) {
Column() {
Text(item.title)
.fontSize(18)
.fontWeight(FontWeight.Medium)
.fontColor('#333333')
Text(item.description)
.fontSize(14)
.fontColor('#666666')
.margin({ top: 4 })
}
.alignItems(HorizontalAlign.Start)
.padding(16)
.width('100%')
.backgroundColor(Color.White)
.borderRadius(8)
.onClick(() => { this.navigateToDetail(item); })
}
build() {
Column() {
// Header
Row() {
Text('{{PageName}}')
.fontSize(24)
.fontWeight(FontWeight.Bold)
Blank()
Button('Add')
.onClick(() => {
router.pushUrl({ url: 'pages/{{PageName}}Create' });
})
}
.width('100%')
.padding(16)
// Content
if (this.isLoading && this.items.length === 0) {
Column() {
LoadingProgress().width(50).height(50)
Text('Loading...').margin({ top: 16 })
}
.width('100%')
.layoutWeight(1)
.justifyContent(FlexAlign.Center)
} else if (this.items.length === 0) {
Column() {
Text('No items found')
.fontSize(16)
.fontColor('#999999')
Button('Refresh')
.margin({ top: 16 })
.onClick(() => { this.loadItems(); })
}
.width('100%')
.layoutWeight(1)
.justifyContent(FlexAlign.Center)
} else {
Refresh({ refreshing: $$this.isRefreshing }) {
List({ space: 12 }) {
ForEach(this.items, (item: {{ItemType}}) => {
ListItem() {
this.ItemCard(item)
}
}, (item: {{ItemType}}) => item.id)
}
.width('100%')
.padding({ left: 16, right: 16 })
}
.onRefreshing(() => { this.refreshItems(); })
.layoutWeight(1)
}
}
.width('100%')
.height('100%')
.backgroundColor('#f5f5f5')
}
}

View File

@@ -0,0 +1,503 @@
# HarmonyOS API Reference
Common HarmonyOS APIs for ArkTS development.
## Table of Contents
1. [Router Navigation](#router-navigation)
2. [HTTP Networking](#http-networking)
3. [Preferences Storage](#preferences-storage)
4. [File Operations](#file-operations)
5. [Device Info](#device-info)
6. [Prompt & Dialog](#prompt--dialog)
7. [Media](#media)
---
## Router Navigation
```typescript
import { router } from '@kit.ArkUI';
```
### Push Page
```typescript
router.pushUrl({
url: 'pages/DetailPage',
params: {
id: 123,
title: 'Detail'
}
});
```
### Push with Mode
```typescript
// Standard mode (default) - adds to stack
router.pushUrl({
url: 'pages/Page',
}, router.RouterMode.Standard);
// Single mode - reuses if exists
router.pushUrl({
url: 'pages/Page',
}, router.RouterMode.Single);
```
### Replace Page
```typescript
router.replaceUrl({
url: 'pages/NewPage'
});
```
### Back Navigation
```typescript
// Back to previous
router.back();
// Back to specific page
router.back({
url: 'pages/HomePage'
});
// Back with result
router.back({
url: 'pages/HomePage',
params: { result: 'success' }
});
```
### Get Parameters
```typescript
// In target page
aboutToAppear(): void {
const params = router.getParams() as Record<string, Object>;
if (params) {
const id = params['id'] as number;
const title = params['title'] as string;
}
}
```
### Get Router State
```typescript
const state = router.getState();
console.log('Current page:', state.name);
console.log('Page path:', state.path);
console.log('Stack index:', state.index);
```
### Clear Router Stack
```typescript
router.clear();
```
---
## HTTP Networking
```typescript
import { http } from '@kit.NetworkKit';
```
### GET Request
```typescript
async function getData(): Promise<void> {
const httpRequest = http.createHttp();
try {
const response = await httpRequest.request(
'https://api.example.com/data',
{
method: http.RequestMethod.GET,
header: {
'Content-Type': 'application/json'
},
connectTimeout: 60000,
readTimeout: 60000
}
);
if (response.responseCode === 200) {
const data = JSON.parse(response.result as string);
console.log('Data:', JSON.stringify(data));
}
} catch (error) {
console.error('Request failed:', error);
} finally {
httpRequest.destroy();
}
}
```
### POST Request
```typescript
async function postData(body: object): Promise<void> {
const httpRequest = http.createHttp();
try {
const response = await httpRequest.request(
'https://api.example.com/submit',
{
method: http.RequestMethod.POST,
header: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token'
},
extraData: JSON.stringify(body)
}
);
console.log('Response code:', response.responseCode);
} finally {
httpRequest.destroy();
}
}
```
### Request Options
```typescript
interface HttpRequestOptions {
method: http.RequestMethod; // GET, POST, PUT, DELETE, etc.
header?: Object; // Request headers
extraData?: string | Object; // Request body
connectTimeout?: number; // Connection timeout (ms)
readTimeout?: number; // Read timeout (ms)
expectDataType?: http.HttpDataType;
}
```
---
## Preferences Storage
```typescript
import { preferences } from '@kit.ArkData';
```
### Get Preferences Instance
```typescript
// In component with context
const dataPreferences = await preferences.getPreferences(
this.context,
'myPreferencesStore'
);
```
### Write Data
```typescript
await dataPreferences.put('username', 'John');
await dataPreferences.put('age', 25);
await dataPreferences.put('isVip', true);
await dataPreferences.put('scores', [90, 85, 92]);
await dataPreferences.flush(); // Persist to disk
```
### Read Data
```typescript
// With default values
const username = await dataPreferences.get('username', '') as string;
const age = await dataPreferences.get('age', 0) as number;
const isVip = await dataPreferences.get('isVip', false) as boolean;
```
### Check Key Exists
```typescript
const hasKey = await dataPreferences.has('username');
```
### Delete Data
```typescript
await dataPreferences.delete('username');
await dataPreferences.flush();
```
### Clear All
```typescript
await dataPreferences.clear();
await dataPreferences.flush();
```
### Delete Preferences File
```typescript
await preferences.deletePreferences(this.context, 'myPreferencesStore');
```
---
## File Operations
```typescript
import { fileIo as fs } from '@kit.CoreFileKit';
```
### Get Application Paths
```typescript
// In AbilityStage or UIAbility
const filesDir = this.context.filesDir; // /data/app/.../files
const cacheDir = this.context.cacheDir; // /data/app/.../cache
const tempDir = this.context.tempDir; // /data/app/.../temp
```
### Write File
```typescript
const filePath = `${this.context.filesDir}/data.txt`;
const file = fs.openSync(filePath, fs.OpenMode.CREATE | fs.OpenMode.WRITE_ONLY);
fs.writeSync(file.fd, 'Hello, HarmonyOS!');
fs.closeSync(file);
```
### Read File
```typescript
const filePath = `${this.context.filesDir}/data.txt`;
const file = fs.openSync(filePath, fs.OpenMode.READ_ONLY);
const buffer = new ArrayBuffer(4096);
const readLen = fs.readSync(file.fd, buffer);
const content = String.fromCharCode(...new Uint8Array(buffer.slice(0, readLen)));
fs.closeSync(file);
```
### Check File Exists
```typescript
const exists = fs.accessSync(filePath);
```
### Delete File
```typescript
fs.unlinkSync(filePath);
```
### List Directory
```typescript
const files = fs.listFileSync(this.context.filesDir);
files.forEach((file: string) => {
console.log('File:', file);
});
```
---
## Device Info
```typescript
import { deviceInfo } from '@kit.BasicServicesKit';
```
### Get Device Information
```typescript
const brand = deviceInfo.brand; // e.g., "HUAWEI"
const model = deviceInfo.productModel; // e.g., "Mate 60"
const osVersion = deviceInfo.osFullName; // e.g., "HarmonyOS 5.0"
const sdkVersion = deviceInfo.sdkApiVersion; // e.g., 12
const deviceType = deviceInfo.deviceType; // e.g., "phone", "tablet"
```
---
## Prompt & Dialog
```typescript
import { promptAction } from '@kit.ArkUI';
```
### Toast
```typescript
promptAction.showToast({
message: 'Operation successful',
duration: 2000,
bottom: 80
});
```
### Alert Dialog
```typescript
promptAction.showDialog({
title: 'Confirm',
message: 'Are you sure you want to delete?',
buttons: [
{ text: 'Cancel', color: '#999999' },
{ text: 'Delete', color: '#FF0000' }
]
}).then((result) => {
if (result.index === 1) {
// Delete confirmed
}
});
```
### Action Sheet
```typescript
promptAction.showActionMenu({
title: 'Select Option',
buttons: [
{ text: 'Camera', color: '#000000' },
{ text: 'Gallery', color: '#000000' },
{ text: 'Cancel', color: '#999999' }
]
}).then((result) => {
switch (result.index) {
case 0: // Camera
break;
case 1: // Gallery
break;
}
});
```
### Custom Dialog
```typescript
@CustomDialog
struct ConfirmDialog {
controller: CustomDialogController;
title: string = '';
onConfirm: () => void = () => {};
build() {
Column() {
Text(this.title).fontSize(20).margin({ bottom: 20 })
Row({ space: 20 }) {
Button('Cancel')
.onClick(() => { this.controller.close(); })
Button('Confirm')
.onClick(() => {
this.onConfirm();
this.controller.close();
})
}
}
.padding(20)
}
}
// Usage in component
@Entry
@Component
struct DialogExample {
dialogController: CustomDialogController = new CustomDialogController({
builder: ConfirmDialog({
title: 'Delete Item?',
onConfirm: () => { this.handleDelete(); }
}),
autoCancel: true
});
handleDelete(): void {
// Delete logic
}
build() {
Button('Show Dialog')
.onClick(() => { this.dialogController.open(); })
}
}
```
---
## Media
### Image Picker
```typescript
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { picker } from '@kit.CoreFileKit';
async function pickImage(): Promise<string | null> {
const photoPicker = new picker.PhotoViewPicker();
try {
const result = await photoPicker.select({
MIMEType: picker.PhotoViewMIMETypes.IMAGE_TYPE,
maxSelectNumber: 1
});
if (result.photoUris.length > 0) {
return result.photoUris[0];
}
} catch (error) {
console.error('Pick image failed:', error);
}
return null;
}
```
### Camera Capture
```typescript
import { camera } from '@kit.CameraKit';
// Request camera permission first
// Then use camera APIs for capture
```
### Audio Playback
```typescript
import { media } from '@kit.MediaKit';
async function playAudio(uri: string): Promise<void> {
const player = await media.createAVPlayer();
player.on('stateChange', (state: string) => {
if (state === 'prepared') {
player.play();
}
});
player.url = uri;
}
```
---
## Common Import Patterns
```typescript
// UI Kit
import { router, promptAction } from '@kit.ArkUI';
// Network Kit
import { http } from '@kit.NetworkKit';
// Data Kit
import { preferences } from '@kit.ArkData';
// File Kit
import { fileIo as fs, picker } from '@kit.CoreFileKit';
// Basic Services
import { deviceInfo } from '@kit.BasicServicesKit';
// Media Kit
import { media } from '@kit.MediaKit';
```

View File

@@ -0,0 +1,126 @@
# ArkGuard 代码混淆指南
ArkGuard 是 HarmonyOS 官方推荐的代码混淆工具,用于提升应用安全性,防止逆向分析。
## 环境要求
- **DevEco Studio**: 5.0.3.600 及以上版本
- **项目模型**: 仅支持 Stage 模型
- **生效模式**: 仅在 Release 模式下生效
## 开启混淆
在模块的 `build-profile.json5` 中配置:
```json
{
"arkOptions": {
"obfuscation": {
"ruleOptions": {
"enable": true,
"files": ["./obfuscation-rules.txt"]
},
"consumerFiles": ["./consumer-rules.txt"]
}
}
}
```
## 混淆规则配置
在项目根目录创建 `obfuscation-rules.txt`
```text
# 开启属性混淆
-enable-property-obfuscation
# 开启顶层作用域名称混淆
-enable-toplevel-obfuscation
# 开启文件名混淆
-enable-filename-obfuscation
# 开启导入导出名称混淆
-enable-export-obfuscation
```
## 白名单配置
某些名称不能混淆如动态属性名、API 字段、数据库字段等):
```text
# 保留属性名
-keep-property-name apiKey
-keep-property-name userId
-keep-property-name responseData
# 保留全局名称
-keep-global-name AppConfig
# 保留文件名
-keep-file-name MainPage
-keep-file-name LoginPage
```
## 配置文件说明
| 配置文件 | 作用 | 可修改 | 影响范围 |
|---------|------|:------:|---------|
| `obfuscation-rules.txt` | 本模块编译时的混淆规则 | ✓ | 本模块 |
| `consumer-rules.txt` | 本模块被依赖时的混淆规则(建议仅配置保留项) | ✓ | 依赖此模块的模块 |
| `obfuscation.txt` | HAR/HSP 构建产物,自动生成 | ✗ | 依赖模块 |
## 常用混淆选项
| 选项 | 说明 |
|------|------|
| `-enable-property-obfuscation` | 混淆对象属性名 |
| `-enable-toplevel-obfuscation` | 混淆顶层作用域的变量和函数名 |
| `-enable-filename-obfuscation` | 混淆文件名 |
| `-enable-export-obfuscation` | 混淆导入导出的名称 |
| `-disable-obfuscation` | 临时禁用混淆(用于调试) |
## 白名单选项
| 选项 | 说明 |
|------|------|
| `-keep-property-name <name>` | 保留指定属性名不被混淆 |
| `-keep-global-name <name>` | 保留指定全局名称不被混淆 |
| `-keep-file-name <name>` | 保留指定文件名不被混淆 |
## 问题排查
### 排查步骤
1. **确认是否与混淆相关**: 临时添加 `-disable-obfuscation` 禁用混淆,验证问题是否消失
2. **定位问题字段**: 根据崩溃日志定位被混淆的关键字段
3. **添加白名单**: 将问题字段加入 `-keep-property-name` 白名单
### 常见需要保留的场景
- **网络请求**: 接口传参字段名、响应数据字段名
- **数据库操作**: 表字段名
- **系统 API**: 系统回调参数
- **三方库接口**: 三方库要求的字段名
### 示例:网络请求字段保留
```text
# API 请求/响应字段
-keep-property-name code
-keep-property-name message
-keep-property-name data
-keep-property-name token
-keep-property-name userId
```
## 验证混淆效果
1. 切换到 **Release** 模式编译
2. 检查构建产物
3. 使用反编译工具验证类名/方法名/属性名是否已混淆
4. 测试应用功能是否正常
## 参考
- [华为官方文档 - ArkGuard](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-arkguard)

View File

@@ -0,0 +1,160 @@
# CodeLinter 代码检查工具
codelinter 是 HarmonyOS 的代码检查与修复工具,可集成到门禁或 CI/CD 环境中。
## 命令格式
```bash
codelinter [options] [dir]
```
- `options`: 可选配置参数
- `dir`: 待检查的工程根目录(可选,默认为当前目录)
## 命令参数
| 参数 | 说明 |
|------|------|
| `--config, -c <filepath>` | 指定规则配置文件 (code-linter.json5) |
| `--fix` | 检查同时执行自动修复 |
| `--format, -f <format>` | 输出格式: `default`/`json`/`xml`/`html` |
| `--output, -o <filepath>` | 指定结果保存位置(不在命令行显示) |
| `--version, -v` | 查看版本 |
| `--product, -p <productName>` | 指定生效的 product |
| `--incremental, -i` | 仅检查 Git 增量文件(新增/修改/重命名) |
| `--help, -h` | 查询帮助 |
| `--exit-on, -e <levels>` | 指定返回非零退出码的告警级别 |
## 基本用法
### 在工程根目录下执行
```bash
# 使用默认规则检查当前工程
codelinter
# 指定规则配置文件
codelinter -c ./code-linter.json5
# 检查并自动修复
codelinter -c ./code-linter.json5 --fix
```
### 在非工程目录下执行
```bash
# 检查指定工程目录
codelinter /path/to/project
# 检查多个目录或文件
codelinter dir1 dir2 file1.ets
# 指定规则文件和工程目录
codelinter -c /path/to/code-linter.json5 /path/to/project
# 检查并修复指定工程
codelinter -c ./code-linter.json5 /path/to/project --fix
```
## 输出格式
```bash
# 默认文本格式输出到命令行
codelinter /path/to/project
# JSON 格式输出
codelinter /path/to/project -f json
# HTML 格式保存到文件
codelinter /path/to/project -f html -o ./report.html
# XML 格式保存到文件
codelinter /path/to/project -f xml -o ./report.xml
```
## 增量检查
对 Git 工程中的增量文件执行检查(仅检查新增、修改、重命名的文件):
```bash
codelinter -i
codelinter --incremental
```
## 指定 Product
当工程存在多个 product 时,指定生效的 product
```bash
codelinter -p free /path/to/project
codelinter --product default
```
## 退出码 (--exit-on)
用于 CI/CD 中根据告警级别控制流程。告警级别:`error``warn``suggestion`
退出码计算方式3位二进制数从高到低表示 error, warn, suggestion
| 配置 | 检查结果包含 | 二进制 | 退出码 |
|------|-------------|--------|--------|
| `--exit-on error` | error, warn, suggestion | 100 | 4 |
| `--exit-on error` | warn, suggestion | 000 | 0 |
| `--exit-on error,warn` | error, warn | 110 | 6 |
| `--exit-on error,warn,suggestion` | error | 100 | 4 |
| `--exit-on error,warn,suggestion` | error, warn, suggestion | 111 | 7 |
```bash
# 仅 error 级别返回非零退出码
codelinter --exit-on error
# error 和 warn 级别返回非零退出码
codelinter --exit-on error,warn
# 所有级别都返回非零退出码
codelinter --exit-on error,warn,suggestion
```
## CI/CD 集成示例
```bash
# 完整的 CI 检查流程
codelinter -c ./code-linter.json5 \
-f json \
-o ./codelinter-report.json \
--exit-on error,warn
# 增量检查(仅检查变更文件)
codelinter -i -c ./code-linter.json5 --exit-on error
# 检查并自动修复,生成 HTML 报告
codelinter -c ./code-linter.json5 \
--fix \
-f html \
-o ./codelinter-report.html
```
## 规则配置文件 (code-linter.json5)
默认规则清单可在检查完成后,根据命令行提示查看生成的 `code-linter.json5` 文件。
示例配置:
```json5
{
"files": [
"**/*.ets",
"**/*.ts"
],
"ignore": [
"**/node_modules/**",
"**/oh_modules/**",
"**/build/**"
],
"ruleSet": ["plugin:@ohos/recommended"],
"rules": {
"@ohos/no-any": "error",
"@ohos/no-console": "warn"
}
}
```

View File

@@ -0,0 +1,521 @@
# ArkUI Component Patterns
Advanced component patterns and best practices for ArkTS development.
## Table of Contents
1. [Component Structure](#component-structure)
2. [State Management Patterns](#state-management-patterns)
3. [Parent-Child Communication](#parent-child-communication)
4. [List Optimization](#list-optimization)
5. [Custom Components](#custom-components)
6. [Conditional Rendering](#conditional-rendering)
---
## Component Structure
### Basic Component
```typescript
@Component
struct MyComponent {
// Private properties
private readonly TAG: string = 'MyComponent';
// State properties
@State isLoading: boolean = false;
// Props from parent
@Prop title: string = '';
// Lifecycle
aboutToAppear(): void {
console.log(this.TAG, 'aboutToAppear');
}
// Build method (required)
build() {
Column() {
Text(this.title)
}
}
}
```
### Entry Component (Page)
```typescript
@Entry
@Component
struct HomePage {
@State currentTab: number = 0;
onPageShow(): void {
// Called when page becomes visible
}
onPageHide(): void {
// Called when page becomes hidden
}
onBackPress(): boolean {
// Return true to prevent default back behavior
return false;
}
build() {
Navigation() {
// Page content
}
.title('Home')
}
}
```
---
## State Management Patterns
### @State - Component Internal State
```typescript
@Component
struct Counter {
@State count: number = 0;
build() {
Column() {
Text(`Count: ${this.count}`)
Button('Increment')
.onClick(() => { this.count++; })
}
}
}
```
### @Prop - One-Way Binding (Parent → Child)
```typescript
// Child component
@Component
struct DisplayCard {
@Prop title: string = '';
@Prop value: number = 0;
build() {
Column() {
Text(this.title).fontSize(16)
Text(`${this.value}`).fontSize(24)
}
}
}
// Parent component
@Entry
@Component
struct Dashboard {
@State temperature: number = 25;
build() {
Column() {
DisplayCard({ title: 'Temperature', value: this.temperature })
Button('Update')
.onClick(() => { this.temperature++; })
}
}
}
```
### @Link - Two-Way Binding
```typescript
// Child component
@Component
struct EditableInput {
@Link inputValue: string;
build() {
TextInput({ text: this.inputValue })
.onChange((value: string) => {
this.inputValue = value;
})
}
}
// Parent component
@Entry
@Component
struct FormPage {
@State username: string = '';
build() {
Column() {
Text(`Username: ${this.username}`)
EditableInput({ inputValue: $username }) // Note: $ prefix
}
}
}
```
### @Provide/@Consume - Cross-Level State
```typescript
// Ancestor component
@Entry
@Component
struct App {
@Provide('theme') theme: string = 'light';
build() {
Column() {
SettingsPage()
Button('Toggle Theme')
.onClick(() => {
this.theme = this.theme === 'light' ? 'dark' : 'light';
})
}
}
}
// Descendant component (any depth)
@Component
struct ThemedCard {
@Consume('theme') theme: string;
build() {
Column() {
Text('Card Content')
.fontColor(this.theme === 'light' ? Color.Black : Color.White)
}
.backgroundColor(this.theme === 'light' ? Color.White : Color.Black)
}
}
```
### @Observed/@ObjectLink - Nested Object Observation
```typescript
// Observable class
@Observed
class Task {
id: number;
title: string;
completed: boolean;
constructor(id: number, title: string) {
this.id = id;
this.title = title;
this.completed = false;
}
}
// Child component with object link
@Component
struct TaskItem {
@ObjectLink task: Task;
build() {
Row() {
Checkbox()
.select(this.task.completed)
.onChange((value: boolean) => {
this.task.completed = value;
})
Text(this.task.title)
.decoration({
type: this.task.completed ? TextDecorationType.LineThrough : TextDecorationType.None
})
}
}
}
// Parent component
@Entry
@Component
struct TaskList {
@State tasks: Task[] = [
new Task(1, 'Buy groceries'),
new Task(2, 'Read book')
];
build() {
List() {
ForEach(this.tasks, (task: Task) => {
ListItem() {
TaskItem({ task: task })
}
}, (task: Task) => task.id.toString())
}
}
}
```
### @StorageLink/@StorageProp - AppStorage Binding
```typescript
// Initialize in EntryAbility
AppStorage.setOrCreate('userToken', '');
AppStorage.setOrCreate('isLoggedIn', false);
// Component with storage binding
@Entry
@Component
struct ProfilePage {
@StorageLink('userToken') token: string = ''; // Two-way
@StorageProp('isLoggedIn') isLoggedIn: boolean = false; // One-way
build() {
Column() {
if (this.isLoggedIn) {
Text('Welcome!')
Button('Logout')
.onClick(() => {
this.token = '';
AppStorage.set('isLoggedIn', false);
})
} else {
Text('Please login')
}
}
}
}
```
---
## Parent-Child Communication
### Events via Callback
```typescript
// Child component
@Component
struct SearchBar {
private onSearch: (query: string) => void = () => {};
@State query: string = '';
build() {
Row() {
TextInput({ placeholder: 'Search...' })
.onChange((value: string) => { this.query = value; })
Button('Search')
.onClick(() => { this.onSearch(this.query); })
}
}
}
// Parent component
@Entry
@Component
struct SearchPage {
@State results: string[] = [];
handleSearch(query: string): void {
// Perform search
this.results = [`Result for: ${query}`];
}
build() {
Column() {
SearchBar({ onSearch: (q: string) => this.handleSearch(q) })
ForEach(this.results, (item: string) => {
Text(item)
})
}
}
}
```
---
## List Optimization
### LazyForEach for Large Lists
```typescript
// Data source implementing IDataSource
class MyDataSource implements IDataSource {
private data: string[] = [];
private listeners: DataChangeListener[] = [];
constructor(data: string[]) {
this.data = data;
}
totalCount(): number {
return this.data.length;
}
getData(index: number): string {
return this.data[index];
}
registerDataChangeListener(listener: DataChangeListener): void {
this.listeners.push(listener);
}
unregisterDataChangeListener(listener: DataChangeListener): void {
const idx = this.listeners.indexOf(listener);
if (idx >= 0) {
this.listeners.splice(idx, 1);
}
}
}
// Component with LazyForEach
@Entry
@Component
struct LargeList {
private dataSource: MyDataSource = new MyDataSource(
Array.from({ length: 10000 }, (_, i) => `Item ${i}`)
);
build() {
List() {
LazyForEach(this.dataSource, (item: string, index: number) => {
ListItem() {
Text(item).fontSize(16).padding(10)
}
}, (item: string) => item)
}
.cachedCount(5) // Number of items to cache
}
}
```
### ForEach Key Function
```typescript
// Always provide a unique key function
ForEach(this.items, (item: Item) => {
ListItem() { ItemCard({ item: item }) }
}, (item: Item) => item.id.toString()) // Unique key
```
---
## Custom Components
### Builder Pattern
```typescript
@Entry
@Component
struct BuilderExample {
@Builder
CardBuilder(title: string, content: string) {
Column() {
Text(title).fontSize(20).fontWeight(FontWeight.Bold)
Text(content).fontSize(14)
}
.padding(16)
.backgroundColor(Color.White)
.borderRadius(8)
}
build() {
Column({ space: 16 }) {
this.CardBuilder('Card 1', 'Content 1')
this.CardBuilder('Card 2', 'Content 2')
}
.padding(16)
}
}
```
### BuilderParam for Slot Pattern
```typescript
@Component
struct Card {
@BuilderParam content: () => void = this.defaultContent;
@Builder
defaultContent() {
Text('Default Content')
}
build() {
Column() {
this.content()
}
.padding(16)
.backgroundColor(Color.White)
.borderRadius(8)
}
}
// Usage
@Entry
@Component
struct SlotExample {
build() {
Column() {
Card() {
Column() {
Text('Custom Title')
Image($r('app.media.icon'))
}
}
}
}
}
```
---
## Conditional Rendering
### if/else
```typescript
@Component
struct ConditionalExample {
@State isLoggedIn: boolean = false;
build() {
Column() {
if (this.isLoggedIn) {
Text('Welcome back!')
Button('Logout')
} else {
Text('Please login')
Button('Login')
.onClick(() => { this.isLoggedIn = true; })
}
}
}
}
```
### Visibility Control
```typescript
@Component
struct VisibilityExample {
@State showDetails: boolean = false;
build() {
Column() {
Text('Summary')
Text('Detailed information...')
.visibility(this.showDetails ? Visibility.Visible : Visibility.None)
Button(this.showDetails ? 'Hide' : 'Show')
.onClick(() => { this.showDetails = !this.showDetails; })
}
}
}
```
---
## Best Practices
1. **Minimize @State scope** - Keep state as close to where it's used as possible
2. **Use @Prop for read-only data** - Prevents accidental modifications
3. **Prefer @Link for form inputs** - Enables two-way binding
4. **Use LazyForEach for lists > 100 items** - Improves performance
5. **Always provide key functions** - Enables efficient list updates
6. **Use @Builder for reusable UI blocks** - Reduces duplication
7. **Clean up in aboutToDisappear** - Cancel timers, unsubscribe events

View File

@@ -0,0 +1,153 @@
# 堆栈解析工具 (hstack)
hstack 是用于将 Release 应用混淆后的 crash 堆栈解析为源码对应堆栈的工具,支持 Windows、Mac、Linux 三个平台。
## 命令格式
```bash
hstack [options]
```
## 命令参数
| 参数 | 说明 |
|------|------|
| `-i, --input` | 指定 crash 文件归档目录 |
| `-c, --crash` | 指定一条 crash 堆栈 |
| `-o, --output` | 指定解析结果输出目录(使用 `-c` 时指定输出文件) |
| `-s, --sourcemapDir` | 指定 sourcemap 文件归档目录 |
| `--so, --soDir` | 指定 shared object (.so) 文件归档目录 |
| `-n, --nameObfuscation` | 指定 nameCache 文件归档目录 |
| `-v, --version` | 查看版本 |
| `-h, --help` | 查询帮助 |
## 参数约束
- crash 文件目录 (`-i`) 与 crash 堆栈 (`-c`) **必须且只能提供一项**
- sourcemap (`-s`) 与 shared object (`--so`) 目录**至少提供一项**
- 如需还原混淆的方法名,需**同时提供** sourcemap 和 nameCache 文件
- 路径参数不支持特殊字符:`` `~!@#$^&*=|{};,\s\[\]<>? ``
## 环境配置
1. 将 Command Line Tools 的 `bin` 目录配置到 PATH 环境变量
2. 配置 Node.js 到环境变量
3. 解析 C++ 异常需配置 SDK 的 `native\llvm\bin` 目录到环境变量 `ADDR2LINE_PATH`
## 使用示例
### 解析 crash 文件目录
```bash
# 完整解析命令
hstack -i crashDir -o outputDir -s sourcemapDir --so soDir -n nameCacheDir
# 仅使用 sourcemap 解析 (ArkTS)
hstack -i crashDir -o outputDir -s sourcemapDir
# 仅使用 so 文件解析 (C++)
hstack -i crashDir -o outputDir --so soDir
# 包含方法名还原
hstack -i crashDir -o outputDir -s sourcemapDir -n nameCacheDir
```
### 解析单条堆栈
```bash
# 输出到控制台
hstack -c "at har (entry|har|1.0.0|src/main/ets/pages/Index.ts:58:58)" -s sourcemapDir
# 输出到文件
hstack -c "at har (entry|har|1.0.0|src/main/ets/pages/Index.ts:58:58)" -s sourcemapDir -o result.txt
```
## 输出说明
- 解析结果输出到 `-o` 指定目录,文件以原始 crash 文件名加 `_` 前缀命名
- 不指定 `-o` 时:
- 使用 `-i` 输入:输出到 crashDir 目录
- 使用 `-c` 输入:直接输出到控制台
## 文件获取
### Sourcemap 文件
构建产物中的 sourcemap 文件,包含:
- 路径信息映射
- 行列号映射 (mappings 字段)
- package-info 信息
### NameCache 文件
构建产物中的 nameCache 文件,包含:
- `IdentifierCache`: 标识符混淆映射
- `MemberMethodCache`: 成员方法混淆映射,格式为 `"源码方法名:起始行:结束行": "混淆后方法名"`
### Shared Object (.so) 文件
构建 Release 应用时,默认 so 文件不包含符号表。如需生成包含符号表的 so 文件,在模块 `build-profile.json5` 中配置:
```json5
{
"buildOption": {
"externalNativeOptions": {
"arguments": "-DCMAKE_BUILD_TYPE=RelWithDebInfo"
}
}
}
```
## 堆栈解析原理
### Crash 堆栈格式
```
at har (entry|har|1.0.0|src/main/ets/components/mainpage/MainPage.js:58:58)
at i (entry|entry|1.0.0|src/main/ets/pages/Index.ts:71:71)
```
路径格式:`引用方packageName|被引用方packageName|version|源码相对路径`
### 解析步骤
1. **根据路径信息找到 sourcemap**
- 从路径 `entry|har|1.0.0|src/main/ets/...` 在 entry 模块 sourcemap 中查找对应字段
2. **利用 sourcemap 还原路径和行列号**
- 根据 `sources``mappings` 字段解析
- 如包含 `package-info`,可进行二次解析获取更准确的源码位置
3. **利用 nameCache 还原方法名**
- 查找混淆后方法名对应的所有条目
- 根据还原后的行号范围匹配正确的源码方法名
### 解析示例
原始堆栈:
```
at i (entry|entry|1.0.0|src/main/ets/pages/Index.ts:71:71)
```
还原后:
```
at callHarFunction (entry/src/main/ets/pages/Index.ets:25:3)
```
## CI/CD 集成
```bash
# 自动化解析脚本示例
hstack \
-i ./crash-logs \
-o ./parsed-logs \
-s ./build/sourcemap \
--so ./build/libs \
-n ./build/nameCache
```
## 常见问题
1. **方法名未还原**: 确保同时提供 `-s``-n` 参数
2. **C++ 堆栈未解析**: 检查 `ADDR2LINE_PATH` 环境变量配置
3. **so 文件无符号表**: 配置 `RelWithDebInfo` 构建选项

View File

@@ -0,0 +1,179 @@
# Hvigor 命令行构建工具 (hvigorw)
hvigorw 是 Hvigor 的 wrapper 包装工具,支持自动安装 Hvigor 构建工具和相关插件依赖,以及执行 Hvigor 构建命令。
## 命令格式
```bash
hvigorw [taskNames...] <options>
```
## 编译构建任务
| 任务 | 说明 |
|------|------|
| `clean` | 清理构建产物 build 目录 |
| `assembleHap` | 构建 Hap 应用 |
| `assembleApp` | 构建 App 应用 |
| `assembleHsp` | 构建 Hsp 包 |
| `assembleHar` | 构建 Har 包 |
| `collectCoverage` | 基于打点数据生成覆盖率统计报表 |
## 常用构建参数
| 参数 | 说明 |
|------|------|
| `-p buildMode={debug\|release}` | 指定构建模式。默认Hap/Hsp/Har 为 debugApp 为 release |
| `-p debuggable=true/false` | 覆盖 buildOption 中的 debuggable 配置 |
| `-p product={ProductName}` | 指定 product 进行编译,默认为 default |
| `-p module={ModuleName}@{TargetName}` | 指定模块及 target 编译(需配合 `--mode module` |
| `-p ohos-test-coverage={true\|false}` | 执行测试框架代码覆盖率插桩编译 |
| `-p parameterFile=param.json` | 设置 oh-package.json5 的参数配置文件 |
## 构建示例
```bash
# 清理构建产物
hvigorw clean
# Debug 模式构建 Hap
hvigorw assembleHap -p buildMode=debug
# Release 模式构建 App
hvigorw assembleApp -p buildMode=release
# 构建指定 product
hvigorw assembleHap -p product=free
# 构建指定模块
hvigorw assembleHap -p module=entry@default --mode module
# 构建多个模块
hvigorw assembleHar -p module=library1@default,library2@default --mode module
```
## 测试命令
### Instrument Test (设备测试)
```bash
hvigorw onDeviceTest -p module={moduleName} -p coverage={true|false} -p scope={suiteName}#{methodName}
```
- `module`: 执行测试的模块,缺省执行所有模块
- `coverage`: 是否生成覆盖率报告,默认 true
- `scope`: 测试范围,格式 `{suiteName}#{methodName}``{suiteName}`
- `ohos-debug-asan`: 是否启用 ASan 检测,默认 false (5.19.0+)
**输出路径:**
- 覆盖率报告: `<module-path>/.test/default/outputs/ohosTest/reports`
- 测试结果: `<project>/<module>/.test/default/intermediates/ohosTest/coverage_data/test_result.txt`
### Local Test (本地测试)
```bash
hvigorw test -p module={moduleName} -p coverage={true|false} -p scope={suiteName}#{methodName}
```
**输出路径:**
- 覆盖率报告: `<module-path>/.test/default/outputs/test/reports`
- 测试结果: `<project>/<module>/.test/default/intermediates/test/coverage_data/test_result.txt`
## 日志级别
| 参数 | 说明 |
|------|------|
| `-e, --error` | 设置日志级别为 error |
| `-w, --warn` | 设置日志级别为 warn |
| `-i, --info` | 设置日志级别为 info |
| `-d, --debug` | 设置日志级别为 debug |
| `--stacktrace` | 开启打印异常堆栈信息 |
## 构建分析 (Build Analyzer)
| 参数 | 说明 |
|------|------|
| `--analyze=normal` | 普通模式分析 |
| `--analyze=advanced` | 进阶模式,更详细的任务耗时数据 |
| `--analyze=ultrafine` | 超精细化模式ArkTS 编译详细打点 (6.0.0+) |
| `--analyze=false` | 不启用构建分析 |
| `--config properties.hvigor.analyzeHtml=true` | 生成 HTML 可视化报告到 `.hvigor/report` |
## 守护进程 (Daemon)
| 参数 | 说明 |
|------|------|
| `--daemon` | 启用守护进程 |
| `--no-daemon` | 关闭守护进程(命令行模式推荐) |
| `--stop-daemon` | 关闭当前工程的守护进程 |
| `--stop-daemon-all` | 关闭所有工程的守护进程 |
| `--status-daemon` | 查询所有 Hvigor 守护进程信息 |
| `--max-old-space-size=12345` | 设置老生代内存大小 (MB) |
| `--max-semi-space-size=32` | 设置新生代半空间大小 (MB, 5.18.4+) |
## 性能与内存优化
| 参数 | 说明 |
|------|------|
| `--parallel` / `--no-parallel` | 开启/关闭并行构建(默认开启) |
| `--incremental` / `--no-incremental` | 开启/关闭增量构建(默认开启) |
| `--optimization-strategy=performance` | 性能优先模式,加快构建但占用更多内存 (5.19.2+) |
| `--optimization-strategy=memory` | 内存优先模式(默认)(5.19.2+) |
## 公共命令
| 任务 | 说明 |
|------|------|
| `tasks` | 打印工程各模块包含的任务信息 |
| `taskTree` | 打印工程各模块的任务依赖关系 |
| `prune` | 清除 30 天未使用的缓存并删除 pnpm 未引用包 |
| `buildInfo` | 打印 build-profile.json5 配置信息 (5.18.4+) |
### buildInfo 扩展参数
```bash
# 打印工程级配置
hvigorw buildInfo
# 打印指定模块配置
hvigorw buildInfo -p module=entry
# 包含 buildOption 配置
hvigorw buildInfo -p buildOption
# JSON 格式输出
hvigorw buildInfo -p json
```
## 其他参数
| 参数 | 说明 |
|------|------|
| `-h, --help` | 打印帮助信息 |
| `-v, --version` | 打印版本信息 |
| `-s, --sync` | 同步工程信息到 `./hvigor/outputs/sync/output.json` |
| `-m, --mode` | 指定执行目录级别 (如 `-m project`) |
| `--type-check` | 开启 hvigorfile.ts 类型检查 |
| `--watch` | 观察模式,用于预览和热加载 |
| `--node-home <string>` | 指定 Node.js 路径 |
| `--config, -c` | 指定 hvigor-config.json5 参数 |
## CI/CD 常用命令组合
```bash
# 完整的 Release 构建流程
hvigorw clean && hvigorw assembleApp -p buildMode=release --no-daemon
# 带构建分析的 Debug 构建
hvigorw assembleHap -p buildMode=debug --analyze=advanced --no-daemon
# 运行测试并生成覆盖率报告
hvigorw onDeviceTest -p coverage=true --no-daemon
# 内存受限环境构建
hvigorw assembleHap --optimization-strategy=memory --no-daemon
# 清理缓存
hvigorw prune
hvigorw --stop-daemon-all
```

View File

@@ -0,0 +1,407 @@
# TypeScript to ArkTS Migration Guide
Complete guide for migrating TypeScript code to ArkTS, covering all language constraints and adaptation rules.
## Table of Contents
1. [Overview](#overview)
2. [Constraint Categories](#constraint-categories)
3. [Prohibited Features](#prohibited-features)
4. [Migration Examples](#migration-examples)
5. [Migration Checklist](#migration-checklist)
---
## Overview
ArkTS is based on TypeScript but enforces stricter rules for:
- **Performance**: Static analysis enables AOT compilation
- **Type Safety**: Eliminates runtime type errors
- **Predictability**: Fixed object structures at compile time
Constraints are categorized as:
- **Error**: Must fix, blocks compilation
- **Warning**: Should fix, may become errors in future
---
## Constraint Categories
### 1. Type System Constraints
#### Prohibited: `any` and `unknown`
```typescript
// ❌ TypeScript
let value: any = getData();
let result: unknown = parse(input);
// ✅ ArkTS
interface Data { id: number; name: string; }
let value: Data = getData();
let result: Data | null = parse(input);
```
#### Prohibited: Type assertions to `any`
```typescript
// ❌ TypeScript
(obj as any).dynamicProp = value;
// ✅ ArkTS - Define complete interface
interface MyObject {
existingProp: string;
dynamicProp?: number;
}
let obj: MyObject = { existingProp: 'test' };
obj.dynamicProp = value;
```
### 2. Variable Declaration
#### Prohibited: `var`
```typescript
// ❌ TypeScript
var count = 0;
var name = "hello";
// ✅ ArkTS
let count = 0;
const name = "hello";
```
### 3. Object Structure Constraints
#### Prohibited: Runtime property modification
```typescript
class Point {
x: number = 0;
y: number = 0;
}
let p = new Point();
// ❌ All prohibited
p['z'] = 99; // Dynamic property
delete p.x; // Property deletion
Object.assign(p, {z: 1}); // Runtime extension
// ✅ Define all properties upfront
class Point3D {
x: number = 0;
y: number = 0;
z: number = 0;
}
```
#### Prohibited: Structural typing (duck typing)
```typescript
interface Named { name: string; }
// ❌ TypeScript allows structural matching
let obj = { name: "Alice", age: 25 };
let named: Named = obj; // Works in TS, fails in ArkTS
// ✅ ArkTS requires explicit implementation
class Person implements Named {
name: string = "";
age: number = 0;
}
let named: Named = new Person();
```
### 4. Private Fields
#### Prohibited: `#` private fields
```typescript
// ❌ TypeScript
class MyClass {
#secret: string = "";
#getValue(): string { return this.#secret; }
}
// ✅ ArkTS
class MyClass {
private secret: string = "";
private getValue(): string { return this.secret; }
}
```
### 5. Symbol Properties
#### Prohibited: Symbol as property key
```typescript
// ❌ TypeScript
const sym = Symbol('key');
let obj = { [sym]: 'value' };
// ✅ ArkTS
let obj = { key: 'value' };
```
### 6. Prohibited Statements
#### `for...in`
```typescript
// ❌ TypeScript
for (let key in obj) {
console.log(obj[key]);
}
// ✅ ArkTS - Use Object.keys with forEach
Object.keys(obj).forEach((key: string) => {
// Access via typed interface
});
// ✅ ArkTS - Use for...of for arrays
let arr: string[] = ['a', 'b', 'c'];
for (let item of arr) {
console.log(item);
}
```
#### `delete`
```typescript
// ❌ TypeScript
delete obj.property;
// ✅ ArkTS - Use optional properties
interface Config {
name: string;
value?: number; // Optional, can be undefined
}
let config: Config = { name: 'test', value: undefined };
```
#### `with`
```typescript
// ❌ TypeScript
with (obj) {
console.log(property);
}
// ✅ ArkTS - Use explicit references
console.log(obj.property);
```
#### `in` operator for type checking
```typescript
// ❌ TypeScript
if ('name' in person) {
console.log(person.name);
}
// ✅ ArkTS - Use instanceof
if (person instanceof Person) {
console.log(person.name);
}
// ✅ ArkTS - Use discriminated unions
interface Person { type: 'person'; name: string; }
interface Animal { type: 'animal'; species: string; }
type Entity = Person | Animal;
function getName(e: Entity): string {
if (e.type === 'person') {
return e.name;
}
return e.species;
}
```
### 7. Interface Constraints
#### Prohibited: Call signatures and construct signatures
```typescript
// ❌ TypeScript
interface Callable {
(x: number): number;
new (s: string): Object;
}
// ✅ ArkTS - Use classes
class Calculator {
calculate(x: number): number {
return x * 2;
}
}
class Factory {
create(s: string): Object {
return { value: s };
}
}
```
### 8. Other Restrictions
| Feature | Status | Alternative |
|---------|--------|-------------|
| Comma expressions | Prohibited (except in `for`) | Separate statements |
| Computed property names | Limited | String literal keys |
| Spread on non-arrays | Limited | Explicit copying |
| `eval()` | Prohibited | Avoid |
| `Function()` constructor | Prohibited | Arrow functions |
| Prototype modification | Prohibited | Class inheritance |
---
## Migration Examples
### Example 1: Dynamic Configuration Object
```typescript
// ❌ TypeScript
let config: any = {};
config.apiUrl = 'https://api.example.com';
config.timeout = 5000;
config.retry = true;
// ✅ ArkTS
interface AppConfig {
apiUrl: string;
timeout: number;
retry: boolean;
}
let config: AppConfig = {
apiUrl: 'https://api.example.com',
timeout: 5000,
retry: true
};
```
### Example 2: Object Iteration
```typescript
// ❌ TypeScript
interface User { name: string; age: number; }
let user: User = { name: 'John', age: 30 };
for (let key in user) {
console.log(`${key}: ${user[key]}`);
}
// ✅ ArkTS
interface User {
name: string;
age: number;
}
let user: User = { name: 'John', age: 30 };
console.log(`name: ${user.name}`);
console.log(`age: ${user.age}`);
// Or use explicit property list
const props: (keyof User)[] = ['name', 'age'];
for (let prop of props) {
// Handle each known property
}
```
### Example 3: Optional Property Handling
```typescript
// ❌ TypeScript
let obj: any = { a: 1 };
if (obj.b) {
delete obj.b;
}
obj.c = 3;
// ✅ ArkTS
interface MyObj {
a: number;
b?: number;
c?: number;
}
let obj: MyObj = { a: 1 };
if (obj.b !== undefined) {
obj.b = undefined; // Set to undefined instead of delete
}
obj.c = 3;
```
### Example 4: Type Guards
```typescript
// ❌ TypeScript
function process(input: unknown) {
if (typeof input === 'string') {
return input.toUpperCase();
}
if ('length' in input) {
return (input as any[]).length;
}
}
// ✅ ArkTS
function processString(input: string): string {
return input.toUpperCase();
}
function processArray(input: string[]): number {
return input.length;
}
// Use union types with type narrowing
type Input = string | string[];
function process(input: Input): string | number {
if (typeof input === 'string') {
return input.toUpperCase();
}
return input.length;
}
```
---
## Migration Checklist
### Phase 1: Enable Strict Mode
- [ ] Enable `strict: true` in tsconfig.json
- [ ] Enable `noImplicitAny: true`
- [ ] Enable `strictNullChecks: true`
- [ ] Fix all resulting errors
### Phase 2: Remove Prohibited Keywords
- [ ] Replace all `var` with `let`/`const`
- [ ] Remove all `any` type annotations
- [ ] Remove all `unknown` type annotations
- [ ] Replace `#` private fields with `private`
### Phase 3: Fix Object Patterns
- [ ] Replace dynamic property access with typed interfaces
- [ ] Remove `delete` statements
- [ ] Remove `for...in` loops
- [ ] Remove `with` statements
- [ ] Replace `in` operator type checks
### Phase 4: Update Interfaces
- [ ] Remove call signatures from interfaces
- [ ] Remove construct signatures from interfaces
- [ ] Replace structural typing with explicit implements
### Phase 5: Validate
- [ ] Build with ArkTS compiler
- [ ] Fix remaining errors
- [ ] Test all functionality
---
## Resources
- [Official Migration Guide](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/typescript-to-arkts-migration-guide)
- [ArkTS Language Reference](https://developer.huawei.com/consumer/cn/arkts/)