Skip to content

功能包示例

概述

本文档提供一个完整的功能包示例,帮助你快速理解功能包的开发。

示例 1:用户功能包

目录结构

packages/user/
├── navigation/
│   └── ProfileNav.ets
├── services/
│   ├── datasource/
│   │   ├── UserInfoNetworkDataSource.ets
│   │   └── UserInfoNetworkDataSourceImpl.ets
│   ├── UserInfoRepositoryImpl.ets
│   └── UserNavSvcImpl.ets
├── view/
│   └── ProfilePage.ets
├── viewmodels/
│   └── ProfileViewModel.ets
└── UserModule.ets

契约定义

typescript
// shared/contracts/IUserNavSvc.ets
/**
 * 导航服务抽象,功能包内实现
 */
export interface IUserNavSvc {
    /**
     * 跳转到个人中心
     * @returns {void} 无返回值
     */
    toProfile(): void;
}

// shared/contracts/IUserInfoRepository.ets
/**
 * 定义跨功能包共享的 DI 服务键
 */
export const USER_INFO_REPOSITORY_KEY: string = "userInfoRepository";

export interface IUserInfoRepository {
    /**
     * 获取用户个人信息
     * @returns {Promise<NetworkResult<User>>} 用户信息响应
     */
    getPersonInfo(): Promise<NetworkResult<User>>;
}

服务实现

typescript
// packages/user/services/UserNavSvcImpl.ets
/**
 * @file 导航服务实现
 * @description 本模块的导航服务
 * @author JunBin.Yang
 */

import { IUserNavSvc, UserRoutes } from '@shared/contracts';
import { getContainer, CoreServiceKeys } from '@core/di';
import { NavigationService } from '@core/navigation';

export class UserNavSvcImpl implements IUserNavSvc {
  toProfile(): void {
    const navigation = getContainer().tryResolve<NavigationService>(CoreServiceKeys.NavigationService);
    navigation?.navigateTo(UserRoutes.Profile);
  }
}

// packages/user/services/UserInfoRepositoryImpl.ets
/**
 * @file 用户信息仓库类
 * @description 实现用户信息相关请求
 * @author JunBin.Yang
 */
import { NetworkResult } from "@core/network";
import { User } from "@shared/types";
import { IUserInfoRepository } from "@shared/contracts";
import { UserInfoNetworkDataSource } from "./datasource/UserInfoNetworkDataSource";
import { UserInfoNetworkDataSourceImpl } from "./datasource/UserInfoNetworkDataSourceImpl";

/**
 * 用户信息仓库实现
 * 实现 shared/contracts 中定义的 IUserInfoRepository 接口
 */
export class UserInfoRepositoryImpl implements IUserInfoRepository {
  /**
   * 用户信息网络数据源
   */
  private networkDataSource: UserInfoNetworkDataSource;

  /**
   * 构造函数
   * @param {UserInfoNetworkDataSource} networkDataSource - 可选的用户信息数据源实例
   */
  constructor(networkDataSource?: UserInfoNetworkDataSource) {
    this.networkDataSource = networkDataSource ?? new UserInfoNetworkDataSourceImpl();
  }

  /**
   * 获取用户个人信息
   * @returns {Promise<NetworkResult<User>>} 用户信息响应
   */
  async getPersonInfo(): Promise<NetworkResult<User>> {
    return this.networkDataSource.getPersonInfo();
  }
}

ViewModel

typescript
// packages/user/viewmodels/ProfileViewModel.ets
import { BaseViewModel } from "@core/base";
import { CoreServiceKeys, getContainer } from "@core/di";
import { NavigationService } from "@core/navigation";
import { getUserState, UserState } from "@shared/state";

/**
 * @file 个人中心 ViewModel
 * @author Joker.X
 */
@ObservedV2
export default class ProfileViewModel extends BaseViewModel {
  /**
   * 全局用户状态
   */
  private readonly userState: UserState = getUserState();

  /**
   * 获取展示昵称
   * @returns {ResourceStr} 展示昵称
   */
  getDisplayNickName(): ResourceStr {
    const nickName: string = this.userState.getUserInfo().nickName?.trim() ?? "";
    return nickName.length > 0 ? nickName : $r("app.string.user_profile_nickname_empty");
  }

  /**
   * 获取展示用户 ID
   * @returns {ResourceStr} 展示用户 ID
   */
  getDisplayUserId(): ResourceStr {
    const userId: number = this.userState.getUserInfo().id ?? 0;
    return userId > 0 ? `${userId}` : $r("app.string.user_profile_id_empty");
  }

  /**
   * 获取展示手机号
   * @returns {ResourceStr} 展示手机号
   */
  getDisplayPhone(): ResourceStr {
    const phone: string = this.userState.getUserInfo().phone?.trim() ?? "";
    return phone.length > 0 ? phone : $r("app.string.user_profile_phone_empty");
  }

  /**
   * 执行退出登录
   * @returns {void} 无返回值
   */
  logout(): void {
    this.userState.logout();
    const navigation = getContainer().tryResolve<NavigationService>(CoreServiceKeys.NavigationService);
    navigation?.navigateBack();
  }
}

页面

typescript
// packages/user/view/ProfilePage.ets
import { IBestButton, IBestCell, IBestCellGroup } from "@core/ibestui";
import { ColumnBase, MediumPaddingVerticalScroll, P100, SpaceVerticalLarge } from "@core/designsystem";
import { AppNavDestination } from "@core/components";
import ProfileViewModel from "../viewmodel/ProfileViewModel";

/**
 * @file 个人中心视图
 * @author Joker.X
 */
@ComponentV2
export struct ProfilePage {
  /**
   * 个人中心 ViewModel
   */
  @Local
  private vm: ProfileViewModel = new ProfileViewModel();

  /**
   * 构建个人中心页面
   * @returns {void} 无返回值
   */
  build() {
    AppNavDestination({
      title: $r("app.string.user_profile_title"),
      viewModel: this.vm
    }) {
      this.ProfileContent();
    }
  }

  /**
   * 个人中心内容视图
   * @returns {void} 无返回值
   */
  @Builder
  private ProfileContent() {
    MediumPaddingVerticalScroll() {
      ColumnBase({ widthValue: P100 }) {
        IBestCellGroup({ inset: true, outerMargin: 0 }) {
          IBestCell({
            title: $r("app.string.user_profile_nickname_label"),
            value: this.vm.getDisplayNickName(),
            hasBorder: true
          });
          IBestCell({
            title: $r("app.string.user_profile_id_label"),
            value: this.vm.getDisplayUserId(),
            hasBorder: true
          });
          IBestCell({
            title: $r("app.string.user_profile_phone_label"),
            value: this.vm.getDisplayPhone(),
            hasBorder: false
          });
        };

        SpaceVerticalLarge();

        IBestButton({
          text: $r("app.string.user_profile_logout_action"),
          round: true,
          type: "primary",
          buttonSize: "normal",
          btnWidth: P100,
          onBtnClick: (): void => {
            this.vm.logout();
          }
        });
      };
    };
  }
}

内置功能包

HCompass 内置了以下功能包,可以作为参考:

功能包位置说明
demopackages/demo示例功能包,包含各种示例
authpackages/auth认证功能包
userpackages/user用户功能包
mainpackages/main主页功能包

最佳实践

1. 参考内置功能包

查看内置功能包的实现,学习最佳实践。

2. 保持简单

功能包应该职责单一,不要过于复杂。

3. 编写测试

为功能包编写单元测试和集成测试。

4. 编写文档

为功能包编写 README 文档。

下一步