ArkTS Stage 工程入门:从目录结构到第一个页面

ArkTS Stage 工程入门:从目录结构到第一个页面

HarmonyOS 应用开发刚入门时,最容易卡住的不是语法,而是"不知道代码从哪里开始跑"。打开 DevEco Studio 新建的 ArkTS Stage 工程后,目录里有 app.json5module.json5EntryAbility.etsIndex.etsresources 等文件。每个文件看起来都重要,但它们分别负责什么?页面是怎么被加载出来的?修改哪个地方才能让应用显示自己的界面?

这篇文章站在第一次接触 Stage 模型的读者角度,把官方快速入门文档里的主线重新梳理一遍:先理解工程结构,再看 UIAbility 入口,最后写一个能运行的首页。

1. 为什么 HarmonyOS 推荐 Stage 模型

Stage 模型是 HarmonyOS 当前主推的应用模型。和早期 FA 模型相比,Stage 模型更强调"模块化"和"生命周期清晰":应用可以由多个模块组成,每个模块里声明自己的 Ability,Ability 再负责窗口、页面和系统交互。

对普通开发者来说,可以先记住三个结论:

  1. UIAbility 是应用界面入口,类似"一个可以被系统启动的界面能力"。
  2. WindowStage 是窗口舞台,首页通常通过它加载。
  3. ArkUI 页面 是具体界面,常见文件是 pages/Index.ets

2. 新建工程后先看哪几个文件

一个典型 ArkTS Stage 工程会包含下面这些关键文件。

text 复制代码
entry
├── src
│   └── main
│       ├── ets
│       │   ├── entryability
│       │   │   └── EntryAbility.ets
│       │   └── pages
│       │       └── Index.ets
│       ├── module.json5
│       └── resources
│           ├── base
│           │   ├── element
│           │   ├── media
│           │   └── profile
│           └── rawfile
└── build-profile.json5

这里不要一上来就改很多地方。建议按"配置 -> 入口 -> 页面"的顺序理解:

文件 作用 初学时重点
app.json5 应用级配置 应用名称、版本、图标等
module.json5 模块级配置 声明 Ability、页面路由、权限
EntryAbility.ets UIAbility 入口 生命周期、窗口创建、首页加载
Index.ets ArkUI 首页 页面布局、状态、点击事件
resources 资源目录 字符串、颜色、图片、profile 配置

3. app.json5:应用级配置

app.json5 通常描述整个应用,而不是单个页面。开发时常见的配置包括应用包名、版本、图标、标签等。

json5 复制代码
{
  "app": {
    "bundleName": "com.example.stagequickstart",
    "vendor": "example",
    "versionCode": 1000000,
    "versionName": "1.0.0",
    "icon": "$media:app_icon",
    "label": "$string:app_name"
  }
}

代码解释:

  1. bundleName 是应用包名,正式项目中要保持唯一。
  2. versionCode 面向系统和市场,用数字表示版本递增。
  3. versionName 面向用户展示,比如 1.0.0
  4. iconlabel 使用资源引用,不建议直接写死文件路径或中文。

4. module.json5:模块和 Ability 的声明入口

module.json5 是初学者必须认真看的文件,因为它决定了系统能不能找到你的 UIAbility。

json5 复制代码
{
  "module": {
    "name": "entry",
    "type": "entry",
    "description": "$string:module_desc",
    "mainElement": "EntryAbility",
    "deviceTypes": [
      "phone",
      "tablet",
      "2in1"
    ],
    "abilities": [
      {
        "name": "EntryAbility",
        "srcEntry": "./ets/entryability/EntryAbility.ets",
        "description": "$string:EntryAbility_desc",
        "icon": "$media:app_icon",
        "label": "$string:EntryAbility_label",
        "startWindowIcon": "$media:startIcon",
        "startWindowBackground": "$color:start_window_background",
        "exported": true,
        "skills": [
          {
            "entities": [
              "entity.system.home"
            ],
            "actions": [
              "action.system.home"
            ]
          }
        ]
      }
    ]
  }
}

代码解释:

  1. mainElement 指定模块主入口,一般指向 EntryAbility
  2. srcEntry 指向 UIAbility 源码文件。
  3. exported 表示该 Ability 是否可以被外部启动。
  4. skills 中的 home 能力声明决定应用图标入口能否从桌面启动。
  5. startWindowIconstartWindowBackground 用于启动页展示。

如果桌面点图标后应用没有正常启动,除了看代码报错,也要检查这里的 mainElementsrcEntryskills

5. EntryAbility.ets:应用界面的真正入口

EntryAbility.ets 是 Stage 工程里最关键的入口文件。它继承自 UIAbility,系统启动应用时,会回调它的生命周期方法。

ts 复制代码
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';

const DOMAIN = 0x0000;

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    hilog.info(DOMAIN, 'StageQuickStart', 'EntryAbility onCreate');
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    hilog.info(DOMAIN, 'StageQuickStart', 'WindowStage create');

    windowStage.loadContent('pages/Index', (err) => {
      if (err.code) {
        hilog.error(DOMAIN, 'StageQuickStart', 'loadContent failed: %{public}s', JSON.stringify(err));
        return;
      }
      hilog.info(DOMAIN, 'StageQuickStart', 'loadContent success');
    });
  }

  onForeground(): void {
    hilog.info(DOMAIN, 'StageQuickStart', 'EntryAbility foreground');
  }

  onBackground(): void {
    hilog.info(DOMAIN, 'StageQuickStart', 'EntryAbility background');
  }
}

代码解释:

  1. onCreate 表示 Ability 被创建,适合做轻量初始化。
  2. onWindowStageCreate 表示窗口舞台创建,这里通常加载首页。
  3. windowStage.loadContent('pages/Index') 会加载 pages/Index.ets
  4. onForegroundonBackground 分别对应前台和后台切换。
  5. 日志使用 hilog,调试生命周期时比 console.log 更适合。

6. pages/Index.ets:第一个 ArkUI 页面

首页文件一般放在 entry/src/main/ets/pages/Index.ets。下面写一个简单但完整的页面:标题、说明、计数器按钮、状态展示都有。

ts 复制代码
@Entry
@Component
struct Index {
  @State count: number = 0;

  build() {
    Column({ space: 18 }) {
      Text('ArkTS Stage 快速入门')
        .fontSize(28)
        .fontWeight(FontWeight.Bold)
        .fontColor('#182431')

      Text('这个页面由 EntryAbility 通过 WindowStage 加载。')
        .fontSize(16)
        .fontColor('#5A6B7B')
        .textAlign(TextAlign.Center)

      Text(`当前点击次数:${this.count}`)
        .fontSize(20)
        .fontColor('#0A7F64')

      Button('点击一次')
        .width('80%')
        .height(48)
        .fontSize(18)
        .onClick(() => {
          this.count++;
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .padding(24)
    .backgroundColor('#F7FAFC')
  }
}

代码解释:

  1. @Entry 表示这是一个入口组件。
  2. @Component 表示这是 ArkUI 自定义组件。
  3. @State 修饰的变量变化后会触发界面刷新。
  4. build() 描述页面结构。
  5. Column 是纵向布局容器,适合做入门页面。

7. 初学者要理解的页面加载关系

这一点非常关键:Index.ets 本身不会凭空显示出来,它需要被窗口加载。

ts 复制代码
windowStage.loadContent('pages/Index', (err) => {
  if (err.code) {
    return;
  }
});

这里的 'pages/Index' 不写 .ets 后缀。路径和文件结构必须对应:

text 复制代码
entry/src/main/ets/pages/Index.ets

如果你把页面文件改名为 Home.ets,那么加载路径也要同步改成:

ts 复制代码
windowStage.loadContent('pages/Home');

8. 加一个更接近真实项目的首页布局

真实项目不会只有一个按钮。下面这个首页更接近业务应用:上方展示标题,中间展示功能卡片,底部提供操作入口。

ts 复制代码
@Entry
@Component
struct Index {
  private features: string[] = ['Stage 模型', 'ArkUI 页面', '资源引用', '页面跳转'];

  build() {
    Column({ space: 20 }) {
      this.Header()
      this.FeatureList()
      this.Footer()
    }
    .width('100%')
    .height('100%')
    .padding(20)
    .backgroundColor('#EEF4F8')
  }

  @Builder
  Header() {
    Column({ space: 8 }) {
      Text('HarmonyOS Stage 工程')
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
      Text('先理解入口,再写页面,最后扩展业务。')
        .fontSize(16)
        .fontColor('#667788')
    }
    .width('100%')
    .alignItems(HorizontalAlign.Start)
  }

  @Builder
  FeatureList() {
    Column({ space: 12 }) {
      ForEach(this.features, (item: string) => {
        Row() {
          Text(item)
            .fontSize(18)
            .fontWeight(FontWeight.Medium)
          Blank()
          Text('查看')
            .fontSize(14)
            .fontColor('#0A7F64')
        }
        .width('100%')
        .padding(16)
        .borderRadius(16)
        .backgroundColor(Color.White)
      }, (item: string) => item)
    }
    .width('100%')
    .layoutWeight(1)
  }

  @Builder
  Footer() {
    Button('开始学习')
      .width('100%')
      .height(52)
      .fontSize(18)
  }
}

代码解释:

  1. @Builder 可以把复杂页面拆成多个局部构建函数。
  2. ForEach 用于渲染列表。
  3. Blank() 可以把左右内容撑开。
  4. layoutWeight(1) 可以让中间列表占据剩余空间。
  5. 页面拆分后更容易维护,也更接近真实项目写法。

9. 日志怎么看

生命周期代码写了 hilog.info 后,可以在 DevEco Studio 的 Log 窗口查看。建议给日志设置统一 tag,方便过滤。

ts 复制代码
hilog.info(0x0000, 'StageQuickStart', 'WindowStage create');

调试时重点看三类日志:

  1. onCreate 是否执行。
  2. onWindowStageCreate 是否执行。
  3. loadContent 是否成功。

如果 onCreate 有日志,但 loadContent 失败,多半是页面路径写错或页面编译报错。

10. 常见问题排查

10.1 页面白屏

先检查 EntryAbility.ets 中的路径:

ts 复制代码
windowStage.loadContent('pages/Index');

再检查文件是否真的存在:

text 复制代码
entry/src/main/ets/pages/Index.ets

10.2 桌面图标点不开

优先检查 module.json5

json5 复制代码
"mainElement": "EntryAbility"

以及 abilities 里的 name 是否一致。

10.3 页面状态不刷新

检查变量是否使用了 @State

ts 复制代码
@State count: number = 0;

普通字段变化不会自动触发 UI 刷新。

10.4 改了配置但运行没变化

可以尝试:

  1. 停止当前运行任务。
  2. Clean Project。
  3. 重新 Build。
  4. 重新安装到设备或模拟器。

11. 这篇文章的学习路线

如果你是初学者,建议按下面顺序练习:

  1. 新建 ArkTS Stage 工程。
  2. 找到 EntryAbility.ets
  3. onCreateonWindowStageCreate 里加日志。
  4. 修改 Index.ets 页面标题。
  5. 添加一个 @State 计数器。
  6. 改造页面布局,把页面拆成 HeaderFeatureListFooter
  7. 运行到模拟器或真机。

按照这个顺序练一遍,你会真正理解"工程配置、Ability 入口、ArkUI 页面"之间的关系。

12. 总结

ArkTS Stage 工程入门不应该从背 API 开始,而应该先建立运行链路:

text 复制代码
应用配置 app.json5
        ↓
模块配置 module.json5
        ↓
UIAbility 入口 EntryAbility.ets
        ↓
WindowStage 加载页面
        ↓
ArkUI 页面 Index.ets

只要这条链路清楚,后面学习页面路由、资源管理、生命周期、Want 跳转、多 Ability 协作都会容易很多。

参考资料

  1. 华为开发者文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/start-with-ets-stage
  2. HarmonyOS AbilityKit 文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/abilitykit-overview