一 @hadss/hmrouter & 导航
1.1 路由配置
==>步骤1 安装
ohpm install @hadss/hmrouter
==>步骤2 依赖配置 插件配置
2.1 依赖配置
修改工程根目录下的hvigor/hvigor-config.json5 文件,加入路由编译插件
"dependencies": {
"@hadss/hmrouter-plugin": "^1.2.4" // 保持和 oh-package.json5中安装的版本一致
},
2.2 插件配置
修改工程根目录下的hvigorfile.ts,使用路由编译插件
⚠️⚠️ 特别注意 !!
- 配置最下面的
hvigorfile.ts工程级别 entry模块下也有个hvigorfile.ts不要配置错误entry/hvigorfile.ts
import { appTasks } from '@ohos/hvigor-ohos-plugin';
import { appPlugin } from "@hadss/hmrouter-plugin";
export default {
system: appTasks,
plugins: [appPlugin({ ignoreModuleNames: [ /** 不需要扫描的模块 **/ ] })]
// 别扫描 我自己手动处理
};
==>步骤3 初始化路由框架
在 UIAbility 或者启动框架 AppStartup 中初始化路由框架
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
// 日志开启需在init之前调用,否则会丢失初始化日志
HMRouterMgr.openLog("INFO")
HMRouterMgr.init({
context: this.context
})
}
}
==>步骤4 定义路由入口
HMRouter 依赖系统 Navigation 能力,所以必须在页面中定义一个 HMNavigation 容器
# 1 在 ThirdPageOfNav 页, 定义导航入口页 NavHomePage
# 2 NavOtherPage 页 跳转 NavOtherPage 页
# 3 NavOtherPage 返回 NavOtherPage
// 1. thirdPageOfNav 页面中配置导航
// NavHomePage 为入口页面
// navigationId 为 'mainNavigation'
// 配置转场动画
// 配置 隐藏工具栏及标题
import { HMDefaultGlobalAnimator, HMNavigation } from '@hadss/hmrouter';
import { AttributeUpdater } from '@kit.ArkUI';
@Entry
@Component
struct thirdPageOfNav {
modifier: NavModifier = new NavModifier();
build() {
Column() {
Text('thirdPageOfNav').fontSize(30)
HMNavigation({
navigationId: 'mainNavigation', // 路由栈唯一标识
homePageUrl: 'NavHomePage', // 指定入口页 为 NavHomePage页
options: {
standardAnimator: HMDefaultGlobalAnimator.STANDARD_ANIMATOR, // 标准转场动画
dialogAnimator: HMDefaultGlobalAnimator.DIALOG_ANIMATOR, // 弹窗转场动画
modifier: this.modifier // 导航属性修饰器
}
})
}
.backgroundColor(Color.Red)
.width('100%')
.height('100%')
}
}
// 通过 AttributeUpdater 配置 Navigation 属性
class NavModifier extends AttributeUpdater<NavigationAttribute> {
initializeModifier(instance: NavigationAttribute): void {
instance.mode(NavigationMode.Stack); // 单栏模式
instance.navBarWidth('100%');
// instance.hideTitleBar(true); // 隐藏标题栏
// instance.hideToolBar(true); // 隐藏工具栏
}
}
// 2. NavHomePage 为入口页面
// 注意
// 页面添加 export
// 定义标签 'NavHomePage'
import { HMRouter, HMRouterMgr,HMPopInfo } from '@hadss/hmrouter';
// 使用 @HMRouter 标签定义页面,绑定拦截器、生命周期及自定义转场动画
@HMRouter({
pageUrl: 'NavHomePage', // 标签定义页面
// interceptor: ['PageInterceptor'], // 绑定拦截器
// lifecycle: 'pageLifecycle', // 生命周期
// animator: 'pageAnimator' // 自定义转场动画
})
@Component
export struct NavHomePage{
private param: DetailParam = new DetailParam()
@State receivedResult: string = '暂无返回数据'
aboutToAppear(): void {
// 获取路由传递的参数
const currentParam = HMRouterMgr.getCurrentParam() as DetailParam
if (currentParam !== null) {
this.param = currentParam
}
}
build() {
Column() {
Text('NavHomePage') .fontSize(30)
Text('获取参数:' + '\n'+ this.receivedResult).fontSize(15).backgroundColor(Color.Green).margin({top:30})
Button('跳转到其他页')
.onClick(() => {
// 跳转并传参
HMRouterMgr.push({
navigationId: 'mainNavigation', // 导航id
pageUrl: 'NavOtherPage', // 跳转到目标页面
param: {
id: 111,
name: 'NavHomePage'
} // 携带参数
},{
onResult: (popInfo: HMPopInfo) => { // 跳转到新页面,返回时的回调
const res = popInfo.result as DetailParam
if (res !== null && res !== undefined) {
this.receivedResult = String(res.id) + res.name
console.log(this.receivedResult);
}
console.info(`来源页面:${popInfo.srcPageInfo.name}`) // 获取返回来源页面名称
}
})
})
}
.width('100%') .height('100%')
.justifyContent(FlexAlign.Center)
}
}
// 3. NavOtherPage 当前页
// 注意
// 页面添加 export
// 定义标签 'NavOtherPage'
@HMRouter({ pageUrl: 'NavOtherPage' })
@Component
export struct NavOtherPage {
private param: DetailParam = new DetailParam()
aboutToAppear(): void {
// 获取外传进来的参数
const currentParam = HMRouterMgr.getCurrentParam() as DetailParam
if (currentParam !== null) {
this.param = currentParam
}
}
build() {
Column() {
Text('NavOtherPage') .fontSize(30)
Text(`获取参数为: - ID: ${this.param.id}, 名称: ${this.param.name}`).backgroundColor(Color.Green)
.fontSize(20)
Button('返回上一页')
.onClick(() => {
HMRouterMgr.pop({ navigationId: 'mainNavigation' })
})
Button('带参数返回')
.onClick(() => {
HMRouterMgr.pop({
navigationId: 'mainNavigation',
param: {
id: 222,
name: 'NavOtherPage'
}
})
})
}
.width('100%') .height('100%')
.justifyContent(FlexAlign.Center)
}
}
// 定义参数模型
class DetailParam {
id: number = 0
name: string = ''
}
1.2 页面显示/隐藏监听
// 监听导航栈内 某页面的 页面显示/隐藏 声明周期
// 修改状态栏字体颜色
import { HMLifecycle, HMLifecycleContext, IHMLifecycle } from '@hadss/hmrouter';
import { common } from '@kit.AbilityKit';
// 声明一个名为 LoginLifecycle 的生命周期监听器
@HMLifecycle({ lifecycleName: 'LoginLifecycle' })
export class LoginLifecycle implements IHMLifecycle {
onShown(ctx: HMLifecycleContext): void { // 进入界面
// 修改状态栏文本 颜色为黑色
const uiContext = ctx.uiContext.getHostContext() as common.UIAbilityContext
uiContext.windowStage.getMainWindowSync().setWindowSystemBarProperties({
statusBarContentColor: '#000000'
});
}
// 修改状态栏文本 颜色为白色
onHidden(ctx: HMLifecycleContext): void { // 离开界面
const uiContext = ctx.uiContext.getHostContext() as common.UIAbilityContext
uiContext.windowStage.getMainWindowSync().setWindowSystemBarProperties({
statusBarContentColor: '#FFFFFF'
});
}
}
// LOGIN_PAGE页面 添加页面显示/消失监视器, 配置如下
@HMRouter({ pageUrl: PageConstants.LOGIN_PAGE, lifecycle: 'LoginLifecycle' })
1.3 全局侧滑监听 & web页侧滑监听
// 1 全局页面侧滑 到退出应用
import { HMLifecycle, HMLifecycleContext, IHMLifecycle } from '@hadss/hmrouter';
import showToast from '../../utils/ToastUtils';
@HMLifecycle({ lifecycleName: 'ExitAppLifecycle' })
export class ExitAppLifecycle implements IHMLifecycle {
private lastTime: number = 0;
// 不调用~~~
onBackPressed(ctx: HMLifecycleContext): boolean { //侧滑返回 非页面内
let time = new Date().getTime(); // 获取当前时间
if (time - this.lastTime > 1000) { // 两次点击 间隔大于1s
this.lastTime = time; // 记录本次
showToast('再按一次退出', ctx.uiContext, 1000) // toast按需更换
return true;
} else {
return false;
}
}
}
// 2.1 监听web页面侧滑返回
aboutToAppear(): void {
// 获取当前页面生命周期托管者
const lifecycleOwner = HMRouterMgr.getCurrentLifecycleOwner()
if (lifecycleOwner) {
// 添加页面显示观察者 监听侧滑返回
lifecycleOwner.addObserver(HMLifecycleState.onBackPressed, (ctx) => {
if (this.webController.accessBackward()) { // 有访问记录
this.webController.backward() // 后退
return true
}
return false
});
}
// onBackPressed: 手机实体按键
// onBackClick: 导航返回键
}
// 2.2 页面上导航 返回键
onBackClick: () => {
if (this.webController.accessBackward()) { // 如果有历史记录
this.webController.backward() // web 内部后退
} else {
HMRouterMgr.pop(); // web 退出当前
}
}
// MainPage 页面添加侧滑退出监听, 配置如下
@HMRouter({ pageUrl: 'MainPage', lifecycle: 'ExitAppLifecycle' })
1.4 模态窗口设置
// 配置模态动画
// 定义全局模态动画:从底部向上滑入,无缩放,不透明
const bottomSlideDialogAnimator: IHMAnimator.Effect = new IHMAnimator.Effect({
direction: IHMAnimator.Direction.BOTTOM_TO_TOP, // ← 从底部向上
opacity: { opacity: 1 }, // ← 不透明(无渐变)
scale: { x: 1, y: 1 } // ← 无缩放
});
@Entry
@Component
struct Index {
modifier: NavModifier = new NavModifier();
build() {
Column() {
HMNavigation({
navigationId: 'mainNavigation', // 路由栈唯一标识
homePageUrl: 'GuidePage', // 指定入口页
options: {
standardAnimator: HMDefaultGlobalAnimator.STANDARD_ANIMATOR, // push转场动画
// dialogAnimator: HMDefaultGlobalAnimator.DIALOG_ANIMATOR, // 模态转场动画
dialogAnimator: bottomSlideDialogAnimator, // 模态转场动画(自定义)
modifier: this.modifier // 导航属性修饰器
}
})
}
.backgroundColor($r('app.color.app_Theme_gray'))
.width('100%')
.height('100%')
}
}
// 登录页 配置模态方式进场
@HMRouter({ pageUrl: 'LoginPage', lifecycle: 'LoginLifecycle',dialog: true}) // 标签定义页面
二 @tencent/mmkv & 缓存
- 对标 系统的Preferences
- 为同步存储
ohpm install @tencent/mmkv
// 1 首先需要UIAbility 中注册
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
// 获取应用全局上下文(Application Context)
let appCtx = this.context.getApplicationContext();
//初始化MMKV
let mmkvRootDir = MMKV.initialize(appCtx);
console.info('mmkv rootDir: ', mmkvRootDir);
}
import { MMKV } from '@tencent/mmkv'
// 获取默认实例
const mmkv = MMKV.defaultMMKV()
// 等价于 const defaultMMKV = MMKV.mmkvWithID('mmkv.default')
// 写入
mmkv.encodeBool('isLogin', true)
mmkv.encodeString('userName', '张三')
mmkv.encodeNumber('userId', 1001)
// 读取
let isLogin: boolean = mmkv.decodeBool('isLogin', false)
let userName: string = mmkv.decodeString('userName', '')
let userId: number = mmkv.decodeNumber('userId', 0)
// 删除
mmkv.removeValueForKey('userName')
// 清空
mmkv.clearAll()
import { MMKV } from '@tencent/mmkv'
// 指定 mmapID 创建独立实例,数据互不干扰
const userMMKV = MMKV.mmkvWithID('user_store')
const settingMMKV = MMKV.mmkvWithID('setting_store')
userMMKV.encodeString('token', 'abc123')
settingMMKV.encodeBool('notifyEnabled', true)
// 各实例数据独立存储
let token = userMMKV.decodeString('token', '')
let notify = settingMMKV.decodeBool('notifyEnabled', false)
三 @ohos/axios & 网络请求
ohpm install @ohos/axios
// 添加网络权限
// entry/src/main/module.json5
"requestPermissions": [{
'name': 'ohos permission.INTERNET'
}],
import axios, { AxiosResponse } from '@ohos/axios'
axios.get(bannerPath)
.then((res: AxiosResponse<BannerData>) => {
if ( res.data.data && res.data.data.length >0) { // 注意 res.data 这tm才是原始数据, 三方库在原始数据外又包了一层
this.list = res.data.data
}
console.log('----->' + JSON.stringify(this.list))
})
// 框架请求全数据 网络请求响应Response:
{
"data": {
"data": [
{
"desc": "我们支持订阅啦~",
"id": 30,
"imagePath": "https://www.wanandroid.com/blogimgs/42da12d8-de56-4439-b40c-eab66c227a4b.png",
"isVisible": 1,
"order": 2,
"title": "我们支持订阅啦~",
"type": 0,
"url": "https://www.wanandroid.com/blog/show/3352"
}
//...做了删减
],
"errorCode": 0,
"errorMsg": ""
},
// ⚠️ 注意 'data':{} // {} 为真实数据
"status": 200, // 系统返回
"statusText": "",
"headers": {
"cache-control": "private",
"connection": "close",
"content-type": "application/json;charset=UTF-8",
"date": "Wed, 26 Aug 2026 04:47:22 GMT",
"expires": "Thu, 01 Jan 1970 08:00:00 CST",
"server": "Apache-Coyote/1.1",
"transfer-encoding": "chunked",
"set-cookie": "JSESSIONID=2FD6A009659F9DA513653D39FFF21054; Path=/; Secure; HttpOnly"
},
"config": {
"transitional": {
"silentJSONParsing": true,
"forcedJSONParsing": true,
"clarifyTimeoutError": false
},
"adapter": [
"ohos"
],
"transformRequest": [
null
],
"transformResponse": [
null
],
"timeout": 0,
"xsrfCookieName": "XSRF-TOKEN",
"xsrfHeaderName": "X-XSRF-TOKEN",
"maxContentLength": -1,
"maxBodyLength": -1,
"env": {
"Blob": null
},
"headers": {
"Accept": "application/json, text/plain, */*",
"Content-Type": "application/json",
"Cookie": ""
},
"connectTimeout": 30000,
"readTimeout": 10000,
"checkResultCode": true,
"interceptorHooks": {},
"url": "https://www.wanandroid.com/banner/json",
"method": "get"
},
"request": {},
"performanceTiming": {
"dnsTiming": 0.138,
"firstReceiveTiming": 1033.164,
"firstSendTiming": 989.111,
"redirectTiming": 0,
"responseBodyTiming": 1034.057,
"responseHeaderTiming": 1034.054,
"tcpTiming": 49.778,
"tlsTiming": 989.074,
"totalFinishTiming": 1034.414,
"totalTiming": 1036.889
}
}
// 框架外面包装了一层 data:
// 这是后台的接口
{
"data": [
{
"desc": "我们支持订阅啦~",
"id": 30,
"imagePath": "https://www.wanandroid.com/blogimgs/42da12d8-de56-4439-b40c-eab66c227a4b.png",
"isVisible": 1,
"order": 2,
"title": "我们支持订阅啦~",
"type": 0,
"url": "https://www.wanandroid.com/blog/show/3352"
}
//...做了删减
],
"errorCode": 0,
"errorMsg": ""
}
四 @abner/refresh_v2 & 刷新/加载
ohpm install @abner/refresh_v2
import axiosClient from '../network/AxiosRequest'
import { AxiosHeaders } from '@ohos/axios'
import { ListView } from '@abner/refresh_v2';
import { RefreshController, RefreshDataSource } from "@abner/refresh_v2";
controller: RefreshController = new RefreshController() //刷新控制器,声明全局变量
dataSource: RefreshDataSource = new RefreshDataSource() //数据懒加载操作对象,执行数据增删改查
// 网络请求 -- 下拉刷新
refreshRequest(isRefresh: boolean = false){
this.pageIndex = 0
axiosClient.get<BaseBean<BaseListBean<ArticleBean[]>>>({
url: `${baseurl}article/list/${this.pageIndex}/json?cid=${this.cid}`,
headers: new AxiosHeaders({ 'Cookie': ''}),
showLoading: true
}).then((res)=>{
if ( res.data.datas && res.data.datas.length >0) {
this.list = res.data.datas
this.dataSource.initData(this.list)
}
if (isRefresh) {
this.controller.finishRefresh()
}
})
.catch((error: BusinessError)=>{
if (isRefresh) {
this.controller.finishRefresh()
}
})
}
// 网络请求 -- 上拉下载
loadMoreRequest() {
this.pageIndex++
axiosClient.get<BaseBean<BaseListBean<ArticleBean[]>>>({
url: `${baseurl}article/list/${this.pageIndex}/json?cid=${this.cid}`,
headers: new AxiosHeaders({ 'Cookie': ''}),
showLoading: true
}).then((res)=>{
if ( res.data.datas && res.data.datas.length >0) {
this.list = res.data.datas
this.dataSource.pushDataArray(this.list);
}
this.controller.finishLoadMore() // 停止加载动画
})
.catch((error: BusinessError)=>{
this.pageIndex--
this.controller.finishLoadMore() // // 停止加载动画
})
}
build() {
Column() {
ListView({
lazyDataSource: this.dataSource, // 数据源
itemLayout:(item, index) => {this.itemCell(item as ArticleBean,index)}, // item
controller: this.controller, // 控制器
enableLoadMore: this.loadMore, // 允许下拉刷新
onRefresh: ()=>{
this.refreshRequest(true)
},
onLoadMore: ()=>{
this.loadMoreRequest()
}
})
}
.backgroundColor('#F5F5F5')
.size({width:'100%',height:'100%'})
}
五 @pura/harmony-dialog
"@pura/harmony-dialog": "^1.1.8",