diff --git a/.gitignore b/.gitignore index d2ff201..1a59487 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,9 @@ /.clang-format /.clang-tidy **/.test -/.appanalyzer \ No newline at end of file +/.appanalyzer +# 签名材料(个人 debug 证书,勿提交) +*.p12 +*.cer +*.p7b +**/signing/ diff --git a/AGENTS.md b/AGENTS.md index d2e0084..8580178 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,8 +13,9 @@ HarmonyOS NEXT app. ArkTS + ArkUI declarative UI, hvigor build, DevEco Studio. ## Architecture Overview -单 HAP 模块应用(`entry/`)。页面在 `main_pages.json` 注册;`EntryAbility` 是应用入口 Ability。 -Single-HAP module (`entry/`). Pages registered in `main_pages.json`; `EntryAbility` is the app entry. +单 HAP + 多 HAR 模块化应用:`entry/`(HAP,应用壳 + 业务)、`common/`(HAR,基础设施)、`uikit/`(HAR,通用 UI)。 +Single-HAP + multi-HAR: `entry/` (HAP shell + business), `common/` (HAR infra), `uikit/` (HAR shared UI). +状态管理全量 StateManagement V2(`@ComponentV2/@Local/@Param/@Event/@ObservedV2/@Trace`);导航用 `Navigation` + `NavPathStack`。 详见 `docs/architecture.md`。 ## Directory Structure @@ -22,19 +23,27 @@ Single-HAP module (`entry/`). Pages registered in `main_pages.json`; `EntryAbili ``` harmony-client/ ├── AppScope/ # 应用级配置(bundleName、应用图标/标签) -├── entry/ # 主模块(单 HAP) +├── entry/ # HAP 应用壳 + 业务(唯一入口) │ └── src/ │ ├── main/ -│ │ ├── ets/ # ArkTS 源码 -│ │ │ ├── entryability/ # 应用入口 Ability +│ │ ├── ets/ +│ │ │ ├── entryability/ # 应用入口 Ability(依赖装配) │ │ │ ├── entrybackupability/ # 备份扩展 Ability -│ │ │ └── pages/ # 页面(@Entry 组件) -│ │ ├── resources/ # 资源(字符串/颜色/媒体,用 $r() 引用) +│ │ │ ├── router/ # 路由(AppRouter + RouteMap) +│ │ │ ├── di/ # 依赖注入(ServiceLocator) +│ │ │ ├── model/ # 领域模型(@ObservedV2 + @Trace) +│ │ │ ├── data/ # 数据层(api / repository) +│ │ │ ├── viewmodel/ # 视图模型(@ObservedV2 三态) +│ │ │ ├── view/ # 业务复用组件 +│ │ │ └── pages/ # 页面(@Entry @ComponentV2,仅编排) +│ │ ├── resources/ # 资源(字符串/媒体,用 $r() 引用) │ │ └── module.json5 # 模块清单(Ability 声明、权限) │ ├── test/ # 本地单元测试(hypium) │ ├── ohosTest/ # 设备集成测试(hypium) │ └── mock/ # mock 配置 -├── build-profile.json5 # 构建/签名/产品配置 +├── common/ # HAR 基础设施(网络/日志/状态/工具) +├── uikit/ # HAR 通用 UI 组件库 + 主题 token +├── build-profile.json5 # 构建/签名/产品/模块配置 ├── code-linter.json5 # 代码规范(TS ESLint + 安全/性能规则) ├── hvigorfile.ts # hvigor 构建入口 ├── oh-package.json5 # 依赖声明(hypium / hamock) @@ -48,10 +57,10 @@ harmony-client/ 2. **声明式 UI Declarative UI** — 用状态驱动渲染,禁止命令式直接改 UI。 Drive UI via state; never mutate UI imperatively. 3. **命名 Naming** — 组件/Ability `PascalCase`(文件 `.ets`);变量/函数/属性 `camelCase`;常量 `UPPER_SNAKE_CASE`。 -4. **资源 Resources** — 字符串/颜色/媒体一律 `$r('app.xxx')`,禁止硬编码。 - All strings/colors/media via `$r('app.xxx')`; no hardcoding. -5. **分层 Layering** — 页面只做 UI;业务逻辑放 `service/`,数据模型放 `model/`,复用组件放 `components/`。 - Pages = UI only; logic → `service/`, models → `model/`, reusable components → `components/`. +4. **资源 Resources** — 颜色/间距/字号走 `uikit/theme/Theme.ets` 的 token;用户可见文案优先 `$r('app.string.xxx')`;媒体 `$r('app.media.xxx')`。禁止组件内散落魔法值。 + Colors/spacing/fonts via `uikit/theme/Theme.ets` tokens; copy via `$r('app.string.xxx')`; media via `$r('app.media.xxx')`. No scattered magic values. +5. **分层 Layering** — 页面只编排;业务逻辑放 `viewmodel/`,数据放 `data/`(repository/api),模型放 `model/`,复用组件放 `view/`(通用下沉 `uikit/`)。 + Pages orchestrate only; logic → `viewmodel/`, data → `data/`, models → `model/`, reusable components → `view/` (shared → `uikit/`). 6. **安全 Security** — 遵守 `code-linter.json5` 的 `@security/*` 规则(禁用不安全哈希/AES 等)。 7. **改后必验证 Verify after change** — 每次改动跑 lint + 相关测试,见 `docs/testing.md`。 @@ -66,9 +75,9 @@ harmony-client/ ## Common Tasks -- 加页面 Add a page → `entry/src/main/ets/pages/` + 在 `main_pages.json` 注册 -- 加复用组件 Reusable component → `entry/src/main/ets/components/` -- 写业务逻辑 Business logic → `entry/src/main/ets/service/` +- 加页面 Add a page → `entry/src/main/ets/pages/`(Navigation 路由则加 `router/RouteMap.ets`) +- 加复用组件 Reusable component → `entry/src/main/ets/view/`(跨模块通用 → `uikit/src/main/ets/components/`) +- 写业务逻辑 Business logic → `entry/src/main/ets/viewmodel/` + `entry/src/main/ets/data/` - 加数据模型 Data model → `entry/src/main/ets/model/` - 跑单测 Run tests → `docs/testing.md` diff --git a/docs/build-deploy.md b/docs/build-deploy.md index bad9413..58eb6e5 100644 --- a/docs/build-deploy.md +++ b/docs/build-deploy.md @@ -17,6 +17,20 @@ hvigorw assembleHap --mode module -p product=default -p buildMode=release 产物通常在 `entry/build/default/outputs/` 下(`default` 为 product 名)。 +## 多模块构建 Multi-module Build + +本工程为「单 HAP + 多 HAR」:`entry/`(HAP)+ `common/`(HAR)+ `uikit/`(HAR)。 + +- HAR(`common`/`uikit`)作为 `entry` 的依赖,构建 entry 时会**自动先编译**依赖的 HAR,正常开发只需构建 entry。 +- 任务类型:HAP 用 `assembleHap`,HAR 用 `assembleHar`。 +- 构建整个工程用 `--mode project`(按依赖顺序构建全部模块);构建单个模块用 `--mode module`。 +- 模块注册:根 `build-profile.json5` 的 `modules`;模块间依赖:各自 `oh-package.json5` 的 `dependencies` 用 `file:../xxx` 声明。 + +**HAR 配置要点(易错)**: +- `module.json5` 的 `type` 必须是 `har`(**不是** `shared`——`shared` 对应 HSP,会要求 `hspTasks`)。 +- `oh-package.json5` 的 `main: "Index.ets"` 指向**模块根目录**的 `Index.ets`(该文件 `export * from './src/main/ets/Index.ets'` 转发公共 API)。 +- 权限(如 `ohos.permission.INTERNET`)只声明在 HAP 的 `module.json5` 的 `requestPermissions`;HAR 不声明权限。 + ## 签名 Signing `build-profile.json5` 里 `signingConfigs` 目前为**空**,且 `products[].signingConfig` 引用了 `"default"` —— **签名尚未配置**。 diff --git a/docs/conventions.md b/docs/conventions.md index adac4dc..177a25a 100644 --- a/docs/conventions.md +++ b/docs/conventions.md @@ -21,25 +21,33 @@ ArkTS(TypeScript 的超集,HarmonyOS 严格类型方言)+ ArkUI 声明式 ## 声明式 UI 与状态 Declarative UI & State - UI 由状态驱动:改状态 → 自动重渲染,禁止命令式直接改 UI。 -- 状态装饰器选择: - - 局部状态 `@State` - - 父子传递 `@Prop`(单向)/ `@Link`(双向) - - 跨层 `@Provide` / `@Consume` -- 避免不必要的重渲染:状态粒度要细,不要一个 `@State` 装一整个大对象。 +- 状态管理**全量使用 StateManagement V2**(禁用 V1 装饰器): + - 组件/页面 `@ComponentV2` + - 局部状态 `@Local` + - 父子传递 `@Param`(单向输入)/ `@Event`(子→父回调,**不用 `@Link`**) + - 类可观察 `@ObservedV2` + `@Trace`(字段级精准刷新) + - 跨层共享 `@Provider` / `@Consumer`;变化监听 `@Monitor` + - 全局/持久化 `AppStorageV2` / `PersistenceV2` +- 避免不必要的重渲染:`@Trace` 字段粒度要细,不要一个大对象整体刷新。 ## 资源 Resources -- 字符串、颜色、媒体一律 `$r('app.xxx')`(定义在 `resources/base/element/`)。 -- 禁止在 `.ets` 里硬编码字符串、颜色值、路径。 +- 颜色 / 间距 / 字号统一走 `uikit/theme/Theme.ets` 的 token(`AppColor` / `AppSpacing` / `AppFontSize`),禁止在组件里散落颜色/尺寸魔法值。 +- 用户可见文案:优先 `$r('app.string.xxx')`(便于国际化);临时/演示文案可内联,正式功能应收口到资源。 +- 图片/图标等媒体:`$r('app.media.xxx')`。 ## 组件化 Components -- 页面:`@Entry` 组件,放 `pages/`。 -- 复用组件:`@Component` 或 `@Reusable`(长列表复用),放 `components/`,通过 `@Prop`/事件回调与外界通信,不直接依赖业务层。 +- 页面:`@Entry @ComponentV2`,放 `pages/`,仅编排(组合 view + viewmodel,不含业务逻辑)。 +- 业务复用组件:`@ComponentV2`,放 `view/`,通过 `@Param`(输入)/ `@Event`(回调)与外界通信,不直接依赖业务层。 +- 跨模块通用组件:下沉到 `uikit/`(`uikit/src/main/ets/components/`)。 +- 长列表复用用 `@Reusable`。 ## 分层与依赖方向 Layering -`pages → viewmodel → service → model`。禁止反向依赖(如 `model` import `service`)。 +`pages → viewmodel → data(repository/api) → model`。禁止反向依赖(如 `model` import `repository`)。 +- `viewmodel` 只依赖 `data/repository` 的抽象契约;`repository` 实现依赖 `data/api` + `common` 网络层。 +- 跨模块依赖:`entry → common / uikit`;`common`、`uikit` 相互独立,禁止反向/循环依赖。 架构越界由约定 + 人工审查把关(ArkTS 侧暂无现成的依赖方向 linter)。 ## 安全 Security diff --git a/docs/testing.md b/docs/testing.md index 0f36316..4584d0e 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -6,7 +6,7 @@ | 类型 Kind | 目录 Dir | 说明 Notes | |----------|---------|-----------| -| 本地单元测试 Local unit | `entry/src/test/` | 不依赖设备,跑纯逻辑(service/model 等) | +| 本地单元测试 Local unit | `entry/src/test/` | 不依赖设备,跑纯逻辑(viewmodel/data/model 等) | | 设备集成测试 Device test | `entry/src/ohosTest/` | 依赖真机/模拟器,跑 Ability/UI/系统能力 | 已有示例文件:`LocalUnit.test.ets`、`List.test.ets`(单测);`Ability.test.ets`、`List.test.ets`(设备测试)。 @@ -17,7 +17,7 @@ import { describe, it, expect } from '@ohos/hypium'; export default function exampleTest() { - describe('UserService', () => { + describe('UserRepository', () => { it('should_return_trimmed_name', 0, () => { expect(formatName(' alice ')).assertEqual('alice'); }); @@ -37,7 +37,7 @@ export default function exampleTest() { ## 约定 Conventions -- 每个 `service/`、`model/` 的新逻辑都要有对应的本地单测。 +- 每个 `viewmodel/`、`data/repository/`、`model/` 的新逻辑都要有对应的本地单测。 - 涉及 UI/系统能力/Ability 生命周期 → 设备测试(`ohosTest/`)。 - mock 用 `@ohos/hamock`,不要为了测试改生产代码结构。 - 改动后至少跑受影响的测试再提交。