功能包示例
概述
本文档提供一个完整的功能包示例,帮助你快速理解功能包的开发。
示例 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 内置了以下功能包,可以作为参考:
| 功能包 | 位置 | 说明 |
|---|---|---|
| demo | packages/demo | 示例功能包,包含各种示例 |
| auth | packages/auth | 认证功能包 |
| user | packages/user | 用户功能包 |
| main | packages/main | 主页功能包 |
最佳实践
1. 参考内置功能包
查看内置功能包的实现,学习最佳实践。
2. 保持简单
功能包应该职责单一,不要过于复杂。
3. 编写测试
为功能包编写单元测试和集成测试。
4. 编写文档
为功能包编写 README 文档。