鸿蒙原生开发经验分享大纲
鸿蒙原生开发常见问题汇总,纯干货分享!
咱直接开门见山,聊一聊鸿蒙原生开发里那些常遇到的麻烦事儿,顺便也讲讲对应的解决办法,不管你是刚接触鸿蒙开发的新手,还是已经有一定经验的开发者,希望这篇文章都能帮你少走点弯路。
一、环境搭建
- DevEco Studio安装失败:可能是电脑的系统环境不满足要求,比如内存不够、磁盘空间不足,或者是安装包下载不完整。解决办法就是先检查系统配置,清理出足够的内存和磁盘空间,然后重新下载安装包,最好从官方正规渠道下载,下载的时候确保网络稳定。
- 模拟器启动不了:电脑没开启虚拟化技术是常见原因,还有可能是装了像360安全卫士、火绒这类安全软件,把模拟器的服务禁用了,也可能是模拟器镜像下载不完整或者损坏。解决步骤如下:先重启电脑,进BIOS把虚拟化技术打开(不同品牌电脑进BIOS的快捷键不一样,联想一般按F2,惠普按F10 ,戴尔按F2,华硕按Del );然后把安全软件里拦截模拟器的功能关掉;最后在DevEco Studio里把原来下载的模拟器镜像删了重新下载。
二、代码编写
- ArkTS语法错误:ArkTS是静态类型语言,类型不匹配就很容易出错,比如你定义一个变量是数字类型,结果后面给它赋值成字符串了。还有就是API调用不当,鸿蒙的API在不同版本可能有变化。解决办法就是写代码的时候多注意类型声明,利用好IDE的自动补全和类型提示功能,调用API之前先去官方文档确认一下版本和用法。
- 函数调用没反应:有可能是函数参数传错了,或者函数内部逻辑有问题。遇到这种情况,先检查参数的类型和个数对不对,再在函数内部关键位置打印一些日志,看看程序执行到哪儿出问题了。
三、界面布局
- UI在不同设备上显示错乱:主要是布局没有做响应式设计,用了太多固定尺寸。在鸿蒙开发里,不同设备屏幕尺寸和分辨率都不一样,要是布局不灵活,就会出现按钮、文字显示不正常的情况。解决办法是多用鸿蒙提供的布局组件,像Column、Row、Flex 这些,尺寸单位用vp(视口单位)或者百分比,少用固定像素px 。
- 组件显示不全:可能是组件的层级关系没处理好,被其他组件盖住了,也可能是布局设置有问题,比如宽度、高度设置得不合理。检查一下组件的z - index属性,调整层级,再看看布局的设置,保证组件有足够空间显示。
四、权限相关
- 应用无法获取某些权限:像获取位置信息、相机权限这些,HarmonyOS对敏感权限管控很严格。如果在配置文件里没声明权限,应用就获取不了。要在module.json5文件里的“module”节点下添加“requestPermissions”数组,声明需要的权限,比如获取位置信息权限:
{ "module": { "package": "com.example.harmonyosdemo", "name": ".entry", "type": "entry", "description": "应用入口模块", "mainElement": "EntryAbility", // 新增:位置信息权限声明 "requestPermissions": [ { "name": "ohos.permission.LOCATION", "reason": "需要获取位置信息提供服务", "usedScene": { "ability": ["EntryAbility"], "when": "always" } } ], "abilities": [] } } - 动态权限申请失败:有些权限除了在配置文件声明,还需要动态申请,特别是在Android设备上运行的时候。申请的时候要注意权限申请的回调处理,确保用户同意授权后,应用能正常使用权限。
五、调试相关
- 调试时断点不起作用:有可能是代码优化设置影响了断点,或者是调试配置有问题。在DevEco Studio里,把代码优化选项关掉试试,再检查一下调试配置,确保断点设置在可执行代码行上。
- 日志打印不出来:检查一下日志级别设置,要是设置太高,低级别日志就打印不出来了。还有就是确认一下日志打印的方法有没有写错,比如HiLog的使用,要确保标签、日志级别这些参数都正确。
以上就是鸿蒙原生开发里比较常见的一些问题,希望能帮到大家,祝各位开发顺利,早日开发出超棒的鸿蒙应用!
鸿蒙原生开发常见问题汇总,纯干货分享!
咱直接开门见山,聊一聊鸿蒙原生开发里那些常遇到的麻烦事儿,顺便也讲讲对应的解决办法,不管你是刚接触鸿蒙开发的新手,还是已经有一定经验的开发者,希望这篇文章都能帮你少走点弯路。
一、环境搭建
- DevEco Studio安装失败:可能是电脑的系统环境不满足要求,比如内存不够、磁盘空间不足,或者是安装包下载不完整。解决办法就是先检查系统配置,清理出足够的内存和磁盘空间,然后重新下载安装包,最好从官方正规渠道下载,下载的时候确保网络稳定。
- 模拟器启动不了:电脑没开启虚拟化技术是常见原因,还有可能是装了像360安全卫士、火绒这类安全软件,把模拟器的服务禁用了,也可能是模拟器镜像下载不完整或者损坏。解决步骤如下:先重启电脑,进BIOS把虚拟化技术打开(不同品牌电脑进BIOS的快捷键不一样,联想一般按F2,惠普按F10 ,戴尔按F2,华硕按Del );然后把安全软件里拦截模拟器的功能关掉;最后在DevEco Studio里把原来下载的模拟器镜像删了重新下载。
二、代码编写
- ArkTS语法错误:ArkTS是静态类型语言,类型不匹配就很容易出错,比如你定义一个变量是数字类型,结果后面给它赋值成字符串了。还有就是API调用不当,鸿蒙的API在不同版本可能有变化。解决办法就是写代码的时候多注意类型声明,利用好IDE的自动补全和类型提示功能,调用API之前先去官方文档确认一下版本和用法。
- 函数调用没反应:有可能是函数参数传错了,或者函数内部逻辑有问题。遇到这种情况,先检查参数的类型和个数对不对,再在函数内部关键位置打印一些日志,看看程序执行到哪儿出问题了。
三、界面布局
- UI在不同设备上显示错乱:主要是布局没有做响应式设计,用了太多固定尺寸。在鸿蒙开发里,不同设备屏幕尺寸和分辨率都不一样,要是布局不灵活,就会出现按钮、文字显示不正常的情况。解决办法是多用鸿蒙提供的布局组件,像Column、Row、Flex 这些,尺寸单位用vp(视口单位)或者百分比,少用固定像素px 。
- 组件显示不全:可能是组件的层级关系没处理好,被其他组件盖住了,也可能是布局设置有问题,比如宽度、高度设置得不合理。检查一下组件的z - index属性,调整层级,再看看布局的设置,保证组件有足够空间显示。
四、权限相关
- 应用无法获取某些权限:像获取位置信息、相机权限这些,HarmonyOS对敏感权限管控很严格。如果在配置文件里没声明权限,应用就获取不了。要在module.json5文件里的“module”节点下添加“requestPermissions”数组,声明需要的权限,比如获取位置信息权限:
{ "module": { "package": "com.example.harmonyosdemo", "name": ".entry", "type": "entry", "description": "应用入口模块", "mainElement": "EntryAbility", // 新增:位置信息权限声明 "requestPermissions": [ { "name": "ohos.permission.LOCATION", "reason": "需要获取位置信息提供服务", "usedScene": { "ability": ["EntryAbility"], "when": "always" } } ], "abilities": [] } } - 动态权限申请失败:有些权限除了在配置文件声明,还需要动态申请,特别是在Android设备上运行的时候。申请的时候要注意权限申请的回调处理,确保用户同意授权后,应用能正常使用权限。
五、调试相关
- 调试时断点不起作用:有可能是代码优化设置影响了断点,或者是调试配置有问题。在DevEco Studio里,把代码优化选项关掉试试,再检查一下调试配置,确保断点设置在可执行代码行上。
- 日志打印不出来:检查一下日志级别设置,要是设置太高,低级别日志就打印不出来了。还有就是确认一下日志打印的方法有没有写错,比如HiLog的使用,要确保标签、日志级别这些参数都正确。
以上就是鸿蒙原生开发里比较常见的一些问题,希望能帮到大家,祝各位开发顺利,早日开发出超棒的鸿蒙应用!
收起阅读 »App 上架需要什么?从开发者账号到开心上架(Appuploader)免 Mac 上传的完整流程指南
'''对于初次上架 iOS 应用的开发者来说,“App 上架需要什么?”
往往是最常被问到的问题。
与 Android 市场相比,苹果 App Store 的上架流程更严格、步骤更多。
不仅需要合法的 Apple 开发者账号,还要准备好签名证书、隐私政策、截图描述、IPA 包等。
此外,上传环节传统上依赖 Mac + Xcode,但现在通过 开心上架(Appuploader)命令行工具 即便在 Windows 或 Linux 系统 中,也能轻松完成上架流程。
本文将以实战角度为你详细说明 iOS App 上架所需的全部条件与操作要点。
一、App 上架苹果商店前必备条件总览
要上架 App Store,你至少需要准备以下六项内容:
| 项目 | 说明 |
|---|---|
| Apple Developer 开发者账号 | 负责上架和签名认证 |
| 应用签名证书与描述文件 | 验证 App 合法性 |
| IPA 安装包 | 打包生成的 iOS 应用文件 |
| App 信息与截图 | 用于 App Store 展示 |
| 隐私政策链接 | 审核必需内容 |
| 上传工具 | 将 IPA 提交到 App Store |
每一项都不可缺少,否则应用将无法被苹果审核通过。
二、开发者账号:上架的第一步
注册开发者账号
访问 Apple Developer 官网 并注册账号。
你需要一个 Apple ID,并选择加入 Apple Developer Program(年费 99 美元)。
账号类型如下:
| 类型 | 适合对象 | 特点 |
|---|---|---|
| 个人账号 | 独立开发者 | 成本低、操作简单 |
| 企业账号 | 公司或团队 | 支持多人协作、团队证书共享 |
审核与激活
提交资料后,苹果会通过邮箱验证身份。
审核通过后,你的 Apple 开发者账号即可使用。
三、签名证书与描述文件(Provisioning Profile)
iOS 应用无法像 Android 一样随意打包上传,它必须经过苹果官方签名认证,才能被系统识别与安装。
证书类型:
| 证书名称 | 用途 |
|---|---|
| 开发证书(Development) | 用于测试和调试 |
| 发布证书(Distribution) | 用于 App Store 上架 |
| 推送证书(Push Certificate) | 用于 APNs 推送功能 |
传统方式(麻烦)
需要使用 Xcode + 钥匙串助手生成证书,仅限 Mac 用户操作。
使用 开心上架(Appuploader) 生成证书
- 无需 Xcode;
- 生成速度快;
- 支持多人共享证书文件;
- 团队协作开发更高效。
四、IPA 文件:App 上架的核心载体
IPA 文件相当于 iOS 应用的“安装包”。
无论你使用什么框架(原生、Flutter、uni-app、React Native),都必须最终打包出 .ipa 文件。
打包方式对照:
| 技术栈 | 打包方法 |
|---|---|
| 原生 iOS(Xcode) | Product → Archive → Export |
| uni-app(HBuilderX) | 云打包生成 IPA |
| Flutter / React Native | 命令行构建(需签名文件) |
| Hybrid / Cordova | Xcode 导出或第三方工具 |
对没有 Mac 的开发者而言,uni-app 云打包 + 开心上架 CLI 是最便捷组合。
五、App Store 提交资料与合规要求
在上传 IPA 之前,需要准备以下内容:
| 项目 | 说明 |
|---|---|
| 应用名称 | 符合苹果命名规范,避免关键词堆砌 |
| App 描述 | 介绍应用功能与优势 |
| 关键词 | 有助于搜索排名 |
| 截图 | 必须包含 6.5" 与 5.5" 屏幕尺寸 |
| 隐私政策链接 | 审核强制项,必须能访问 |
| 应用图标 | PNG 格式,1024×1024 像素 |
苹果审核特别重视 隐私与安全声明。
建议在网页托管隐私政策文件(如 GitHub Pages 或自有域名)。
六、上传工具选择与流程
传统上传方式包括:
- Xcode 上传(官方推荐,但仅限 macOS)
- Transporter App(拖拽式上传)
- altool / Fastlane(命令行上传)
这些方式都依赖苹果生态,跨平台开发者无法使用。
推荐方案:开心上架(Appuploader)
支持 图形界面 + 命令行,兼容多系统。
命令行上传示例:
appuploader_cli -u ios@team.com -p xxx-xxx-xxx-xxx -c 2 -f ./build/app.ipa
| 参数 | 含义 |
|---|---|
-u |
Apple 开发者账号 |
-p |
App 专用密码 |
-c |
上传通道(1=旧通道,2=新通道) |
-f |
指定 IPA 文件路径 |
支持:
- 批量上传;
- 上传日志输出;
- 多语言截图与元数据同步;
- 自动化上架脚本集成。
七、App Store Connect 配置与审核发布
IPA 上传完成后,前往 App Store Connect:
填写应用基本信息;
上传截图与隐私政策链接;
选择应用分级(年龄限制);
设置价格与上架区域;
点击 “提交审核”。
审核时间:
- 普通应用:1–3 个工作日;
- 含内购或推送的应用:3–5 天。
八、常见上架问题与解决方法
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 上传失败 Invalid Credentials | 密码错误 | 使用 App 专用密码 |
| “Invalid Bundle ID” | 包名不一致 | 核对 Bundle Identifier |
| 审核拒绝 | 隐私政策或截图问题 | 修改后重新提交 |
| “Missing Provisioning Profile” | 签名配置错误 | 重新生成证书 |
| 上传卡顿 | 网络不稳 | 切换上传通道 -c 1 或 -c 2 |
九、免 Mac 自动化上架实践
你可以将 Fastlane 与 Appuploader CLI 结合,实现全平台的持续集成自动上架。
# 自动构建
fastlane gym --scheme "MyApp" --output_directory "./build"
# 自动上传
appuploader_cli -u dev@icloud.com -p xxx-xxx-xxx-xxx -c 2 -f ./build/MyApp.ipa
支持:
- Jenkins、GitLab CI、GitHub Actions 集成;
- 定时构建 + 自动发布;
- 日志追踪与版本通知。
App 上架需要什么?
需要的不只是账号与证书,更是一套高效的自动化上架流程。
开心上架(Appuploader) 让跨平台上架成为现实,让开发者在任何系统中都能完成从打包、签名到上传审核的全过程。
没有 Mac?没问题。有 Appuploader,就能开心上架。'''
'''对于初次上架 iOS 应用的开发者来说,“App 上架需要什么?”
往往是最常被问到的问题。
与 Android 市场相比,苹果 App Store 的上架流程更严格、步骤更多。
不仅需要合法的 Apple 开发者账号,还要准备好签名证书、隐私政策、截图描述、IPA 包等。
此外,上传环节传统上依赖 Mac + Xcode,但现在通过 开心上架(Appuploader)命令行工具 即便在 Windows 或 Linux 系统 中,也能轻松完成上架流程。
本文将以实战角度为你详细说明 iOS App 上架所需的全部条件与操作要点。
一、App 上架苹果商店前必备条件总览
要上架 App Store,你至少需要准备以下六项内容:
| 项目 | 说明 |
|---|---|
| Apple Developer 开发者账号 | 负责上架和签名认证 |
| 应用签名证书与描述文件 | 验证 App 合法性 |
| IPA 安装包 | 打包生成的 iOS 应用文件 |
| App 信息与截图 | 用于 App Store 展示 |
| 隐私政策链接 | 审核必需内容 |
| 上传工具 | 将 IPA 提交到 App Store |
每一项都不可缺少,否则应用将无法被苹果审核通过。
二、开发者账号:上架的第一步
注册开发者账号
访问 Apple Developer 官网 并注册账号。
你需要一个 Apple ID,并选择加入 Apple Developer Program(年费 99 美元)。
账号类型如下:
| 类型 | 适合对象 | 特点 |
|---|---|---|
| 个人账号 | 独立开发者 | 成本低、操作简单 |
| 企业账号 | 公司或团队 | 支持多人协作、团队证书共享 |
审核与激活
提交资料后,苹果会通过邮箱验证身份。
审核通过后,你的 Apple 开发者账号即可使用。
三、签名证书与描述文件(Provisioning Profile)
iOS 应用无法像 Android 一样随意打包上传,它必须经过苹果官方签名认证,才能被系统识别与安装。
证书类型:
| 证书名称 | 用途 |
|---|---|
| 开发证书(Development) | 用于测试和调试 |
| 发布证书(Distribution) | 用于 App Store 上架 |
| 推送证书(Push Certificate) | 用于 APNs 推送功能 |
传统方式(麻烦)
需要使用 Xcode + 钥匙串助手生成证书,仅限 Mac 用户操作。
使用 开心上架(Appuploader) 生成证书
- 无需 Xcode;
- 生成速度快;
- 支持多人共享证书文件;
- 团队协作开发更高效。
四、IPA 文件:App 上架的核心载体
IPA 文件相当于 iOS 应用的“安装包”。
无论你使用什么框架(原生、Flutter、uni-app、React Native),都必须最终打包出 .ipa 文件。
打包方式对照:
| 技术栈 | 打包方法 |
|---|---|
| 原生 iOS(Xcode) | Product → Archive → Export |
| uni-app(HBuilderX) | 云打包生成 IPA |
| Flutter / React Native | 命令行构建(需签名文件) |
| Hybrid / Cordova | Xcode 导出或第三方工具 |
对没有 Mac 的开发者而言,uni-app 云打包 + 开心上架 CLI 是最便捷组合。
五、App Store 提交资料与合规要求
在上传 IPA 之前,需要准备以下内容:
| 项目 | 说明 |
|---|---|
| 应用名称 | 符合苹果命名规范,避免关键词堆砌 |
| App 描述 | 介绍应用功能与优势 |
| 关键词 | 有助于搜索排名 |
| 截图 | 必须包含 6.5" 与 5.5" 屏幕尺寸 |
| 隐私政策链接 | 审核强制项,必须能访问 |
| 应用图标 | PNG 格式,1024×1024 像素 |
苹果审核特别重视 隐私与安全声明。
建议在网页托管隐私政策文件(如 GitHub Pages 或自有域名)。
六、上传工具选择与流程
传统上传方式包括:
- Xcode 上传(官方推荐,但仅限 macOS)
- Transporter App(拖拽式上传)
- altool / Fastlane(命令行上传)
这些方式都依赖苹果生态,跨平台开发者无法使用。
推荐方案:开心上架(Appuploader)
支持 图形界面 + 命令行,兼容多系统。
命令行上传示例:
appuploader_cli -u ios@team.com -p xxx-xxx-xxx-xxx -c 2 -f ./build/app.ipa
| 参数 | 含义 |
|---|---|
-u |
Apple 开发者账号 |
-p |
App 专用密码 |
-c |
上传通道(1=旧通道,2=新通道) |
-f |
指定 IPA 文件路径 |
支持:
- 批量上传;
- 上传日志输出;
- 多语言截图与元数据同步;
- 自动化上架脚本集成。
七、App Store Connect 配置与审核发布
IPA 上传完成后,前往 App Store Connect:
填写应用基本信息;
上传截图与隐私政策链接;
选择应用分级(年龄限制);
设置价格与上架区域;
点击 “提交审核”。
审核时间:
- 普通应用:1–3 个工作日;
- 含内购或推送的应用:3–5 天。
八、常见上架问题与解决方法
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 上传失败 Invalid Credentials | 密码错误 | 使用 App 专用密码 |
| “Invalid Bundle ID” | 包名不一致 | 核对 Bundle Identifier |
| 审核拒绝 | 隐私政策或截图问题 | 修改后重新提交 |
| “Missing Provisioning Profile” | 签名配置错误 | 重新生成证书 |
| 上传卡顿 | 网络不稳 | 切换上传通道 -c 1 或 -c 2 |
九、免 Mac 自动化上架实践
你可以将 Fastlane 与 Appuploader CLI 结合,实现全平台的持续集成自动上架。
# 自动构建
fastlane gym --scheme "MyApp" --output_directory "./build"
# 自动上传
appuploader_cli -u dev@icloud.com -p xxx-xxx-xxx-xxx -c 2 -f ./build/MyApp.ipa
支持:
- Jenkins、GitLab CI、GitHub Actions 集成;
- 定时构建 + 自动发布;
- 日志追踪与版本通知。
App 上架需要什么?
需要的不只是账号与证书,更是一套高效的自动化上架流程。
开心上架(Appuploader) 让跨平台上架成为现实,让开发者在任何系统中都能完成从打包、签名到上传审核的全过程。
没有 Mac?没问题。有 Appuploader,就能开心上架。'''
收起阅读 »uni-app 也能使用 App.vue 根组件?wot-starter 使用这个插件实现!
📖 背景
wot-starter 是一个基于 UniApp + Vue3 + TypeScript 的跨平台应用开发模板,集成了 Wot UI 组件库。在传统的 UniApp 开发中,由于框架限制,无法像标准 Vue 应用那样使用全局根组件来管理公共状态和组件,这给开发带来了诸多不便。
为了解决这个问题,我们引入了 @uni-ku/root 插件,它通过 Vite 模拟出虚拟根组件,让 UniApp 项目也能享受到类似 Vue 标准应用的开发体验,也可以解决 @uni-helper/vite-plugin-uni-layouts 插件无法使用微信小程序 page-meta 的问题。
🎯 @uni-ku/root 是什么?
@uni-ku/root 借助 Vite 模拟出虚拟根组件(支持SFC的App.vue),解决 uniapp 无法使用公共组件问题。
🎏 支持
- 自定义虚拟根组件文件命名(App.ku.vue文件命名支持更换)
- 更高灵活度的获取虚拟根组件实例(获取KuRootView的Ref)
- 自动提取PageMeta到页面顶层(自动提升小程序PageMeta[用于阻止滚动穿透]组件)
🚀 接入步骤和配置
📦 安装
pnpm add -D @uni-ku/root
yarn add -D @uni-ku/root
npm install -D @uni-ku/root
🚀 Vite 配置
在 vite.config.ts 中引入并配置 UniKuRoot 插件:
import { defineConfig } from 'vite'
import Uni from '@dcloudio/vite-plugin-uni'
import UniHelperManifest from '@uni-helper/vite-plugin-uni-manifest'
import UniHelperPages from '@uni-helper/vite-plugin-uni-pages'
import UniHelperLayouts from '@uni-helper/vite-plugin-uni-layouts'
import UniHelperComponents from '@uni-helper/vite-plugin-uni-components'
import AutoImport from 'unplugin-auto-import/vite'
import { WotResolver } from '@uni-helper/vite-plugin-uni-components/resolvers'
import UniKuRoot from '@uni-ku/root'
// https://vitejs.dev/config/
export default async () => {
const UnoCSS = (await import('unocss/vite')).default
return defineConfig({
plugins: [
// https://github.com/uni-helper/vite-plugin-uni-manifest
UniHelperManifest(),
// https://github.com/uni-helper/vite-plugin-uni-pages
UniHelperPages({
dts: 'src/uni-pages.d.ts',
subPackages: [
'src/subPages',
],
/**
* 排除的页面,相对于 dir 和 subPackages
* @default []
*/
exclude: ['**/components/**/*.*'],
}),
// https://github.com/uni-helper/vite-plugin-uni-layouts
UniHelperLayouts(),
// https://github.com/uni-helper/vite-plugin-uni-components
UniHelperComponents({
resolvers: [WotResolver()],
dts: 'src/components.d.ts',
dirs: ['src/components', 'src/business'],
directoryAsNamespace: true,
}),
// https://github.com/uni-ku/root
UniKuRoot(),
Uni(),
// https://github.com/antfu/unocss
// see unocss.config.ts for config
UnoCSS(),
],
})
}
重要提示:UniKuRoot 插件必须放在 Uni() 插件之前,如果存在修改 pages.json 的插件和 Layout 插件,需要将 UniKuRoot 放在它们之后。
🎉 创建虚拟根组件
在 src/App.ku.vue 中创建虚拟根组件:
注意
App.ku.vue中暂时无法编写样式全局生效,所以我们可以将样式写到App.vue中
<script setup lang="ts">
const { themeVars, theme } = useManualTheme()
</script>
<template>
<wd-config-provider :theme-vars="themeVars" :theme="theme" :custom-class="`page-wraper ${theme}`">
<ku-root-view />
<wd-notify />
<wd-message-box />
<wd-toast />
<global-loading />
<global-toast />
<global-message />
<!-- #ifdef MP-WEIXIN -->
<privacy-popup />
<!-- #endif -->
</wd-config-provider>
</template>
💡 代码示例和使用方法
1. 在页面中使用全局 Toast
<!-- src/pages/index/index.vue -->
<script setup lang="ts">
const globalToast = useGlobalToast()
function showSuccess() {
globalToast.success('操作成功!')
}
function showError() {
globalToast.error('操作失败!')
}
</script>
<template>
<view>
<button @click="showSuccess">显示成功提示</button>
<button @click="showError">显示错误提示</button>
</view>
</template>
2. PageMeta 自动提升示例
在页面中使用 PageMeta 组件,会自动提升到页面顶层:
<!-- src/pages/uni-ku-root/index.vue -->
<script setup lang="ts">
definePage({
name: 'root',
style: {
navigationBarTitleText: 'uni-ku/root',
},
})
const show = ref<boolean>(false)
</script>
<template>
<page-meta :page-style="`overflow:${show ? 'hidden' : 'visible'};`" />
<view class="min-h-200vh">
<demo-block title="锁定滚动" transparent>
<wd-cell-group border>
<wd-cell title="锁定滚动" is-link @click="show = !show" />
</wd-cell-group>
</demo-block>
<wd-popup
v-model="show"
lock-scroll
position="bottom"
closable
:safe-area-inset-bottom="true"
custom-style="height: 200px;"
@close="show = false"
/>
</view>
</template>
🎉 总结
通过接入 @uni-ku/root,wot-starter 项目成功实现了:
- ✅ 全局组件的统一管理
- ✅ 主题配置的全局应用
- ✅ 更好的代码组织结构
- ✅ 接近标准 Vue 应用的开发体验
- ✅ 完美支持 Wot UI 组件库
这个方案不仅解决了 UniApp 开发中的痛点,还可以解决 @uni-helper/vite-plugin-uni-layouts 插件无法使用微信小程序 page-meta 的问题,一举多得,美哉!
🔗 相关链接
- @uni-ku/root GitHub: https://github.com/uni-ku/root
- wot-ui: https://wot-ui.cn
- wot-starter: https://starter.wot-ui.cn
📖 背景
wot-starter 是一个基于 UniApp + Vue3 + TypeScript 的跨平台应用开发模板,集成了 Wot UI 组件库。在传统的 UniApp 开发中,由于框架限制,无法像标准 Vue 应用那样使用全局根组件来管理公共状态和组件,这给开发带来了诸多不便。
为了解决这个问题,我们引入了 @uni-ku/root 插件,它通过 Vite 模拟出虚拟根组件,让 UniApp 项目也能享受到类似 Vue 标准应用的开发体验,也可以解决 @uni-helper/vite-plugin-uni-layouts 插件无法使用微信小程序 page-meta 的问题。
🎯 @uni-ku/root 是什么?
@uni-ku/root 借助 Vite 模拟出虚拟根组件(支持SFC的App.vue),解决 uniapp 无法使用公共组件问题。
🎏 支持
- 自定义虚拟根组件文件命名(App.ku.vue文件命名支持更换)
- 更高灵活度的获取虚拟根组件实例(获取KuRootView的Ref)
- 自动提取PageMeta到页面顶层(自动提升小程序PageMeta[用于阻止滚动穿透]组件)
🚀 接入步骤和配置
📦 安装
pnpm add -D @uni-ku/root
yarn add -D @uni-ku/root
npm install -D @uni-ku/root
🚀 Vite 配置
在 vite.config.ts 中引入并配置 UniKuRoot 插件:
import { defineConfig } from 'vite'
import Uni from '@dcloudio/vite-plugin-uni'
import UniHelperManifest from '@uni-helper/vite-plugin-uni-manifest'
import UniHelperPages from '@uni-helper/vite-plugin-uni-pages'
import UniHelperLayouts from '@uni-helper/vite-plugin-uni-layouts'
import UniHelperComponents from '@uni-helper/vite-plugin-uni-components'
import AutoImport from 'unplugin-auto-import/vite'
import { WotResolver } from '@uni-helper/vite-plugin-uni-components/resolvers'
import UniKuRoot from '@uni-ku/root'
// https://vitejs.dev/config/
export default async () => {
const UnoCSS = (await import('unocss/vite')).default
return defineConfig({
plugins: [
// https://github.com/uni-helper/vite-plugin-uni-manifest
UniHelperManifest(),
// https://github.com/uni-helper/vite-plugin-uni-pages
UniHelperPages({
dts: 'src/uni-pages.d.ts',
subPackages: [
'src/subPages',
],
/**
* 排除的页面,相对于 dir 和 subPackages
* @default []
*/
exclude: ['**/components/**/*.*'],
}),
// https://github.com/uni-helper/vite-plugin-uni-layouts
UniHelperLayouts(),
// https://github.com/uni-helper/vite-plugin-uni-components
UniHelperComponents({
resolvers: [WotResolver()],
dts: 'src/components.d.ts',
dirs: ['src/components', 'src/business'],
directoryAsNamespace: true,
}),
// https://github.com/uni-ku/root
UniKuRoot(),
Uni(),
// https://github.com/antfu/unocss
// see unocss.config.ts for config
UnoCSS(),
],
})
}
重要提示:UniKuRoot 插件必须放在 Uni() 插件之前,如果存在修改 pages.json 的插件和 Layout 插件,需要将 UniKuRoot 放在它们之后。
🎉 创建虚拟根组件
在 src/App.ku.vue 中创建虚拟根组件:
注意
App.ku.vue中暂时无法编写样式全局生效,所以我们可以将样式写到App.vue中
<script setup lang="ts">
const { themeVars, theme } = useManualTheme()
</script>
<template>
<wd-config-provider :theme-vars="themeVars" :theme="theme" :custom-class="`page-wraper ${theme}`">
<ku-root-view />
<wd-notify />
<wd-message-box />
<wd-toast />
<global-loading />
<global-toast />
<global-message />
<!-- #ifdef MP-WEIXIN -->
<privacy-popup />
<!-- #endif -->
</wd-config-provider>
</template>
💡 代码示例和使用方法
1. 在页面中使用全局 Toast
<!-- src/pages/index/index.vue -->
<script setup lang="ts">
const globalToast = useGlobalToast()
function showSuccess() {
globalToast.success('操作成功!')
}
function showError() {
globalToast.error('操作失败!')
}
</script>
<template>
<view>
<button @click="showSuccess">显示成功提示</button>
<button @click="showError">显示错误提示</button>
</view>
</template>
2. PageMeta 自动提升示例
在页面中使用 PageMeta 组件,会自动提升到页面顶层:
<!-- src/pages/uni-ku-root/index.vue -->
<script setup lang="ts">
definePage({
name: 'root',
style: {
navigationBarTitleText: 'uni-ku/root',
},
})
const show = ref<boolean>(false)
</script>
<template>
<page-meta :page-style="`overflow:${show ? 'hidden' : 'visible'};`" />
<view class="min-h-200vh">
<demo-block title="锁定滚动" transparent>
<wd-cell-group border>
<wd-cell title="锁定滚动" is-link @click="show = !show" />
</wd-cell-group>
</demo-block>
<wd-popup
v-model="show"
lock-scroll
position="bottom"
closable
:safe-area-inset-bottom="true"
custom-style="height: 200px;"
@close="show = false"
/>
</view>
</template>
🎉 总结
通过接入 @uni-ku/root,wot-starter 项目成功实现了:
- ✅ 全局组件的统一管理
- ✅ 主题配置的全局应用
- ✅ 更好的代码组织结构
- ✅ 接近标准 Vue 应用的开发体验
- ✅ 完美支持 Wot UI 组件库
这个方案不仅解决了 UniApp 开发中的痛点,还可以解决 @uni-helper/vite-plugin-uni-layouts 插件无法使用微信小程序 page-meta 的问题,一举多得,美哉!
🔗 相关链接
- @uni-ku/root GitHub: https://github.com/uni-ku/root
- wot-ui: https://wot-ui.cn
- wot-starter: https://starter.wot-ui.cn
基于vue3.5+vite7.1+tauri2.9实战桌面端后台管理系统
vue3-tauri2-admin:最新研发vite7.1+tauri2.9+vue3 setup+pinia3+elementPlus跨平台电脑端中后台管理系统Exe模板。包含了表格、图表、表单、列表、编辑器、错误处理等模块。
使用技术
- 开发工具:VScode
- 跨平台框架:Tauri^2.9
- 前端技术框架:vite^7.1.12+vue^3.5.22+vue-router^4.6.3
- 组件库:element-plus^2.11.5
- 状态管理:pinia^3.0.3
- 国际化方案:vue-i18n^11.1.12
- 图表组件:echarts^6.0.0
- markdown编辑器:md-editor-v3^6.1.0
- 富文本编辑器:@vueup/vue-quill^1.2.0
- 模拟数据:mockjs^1.1.0
项目结构目录
使用最新跨平台技术tauri2.9+vue3搭建项目模板页面。
tauri2-vue3admin客户端后台系统已经更新到我的原创作品集。
tauri2.9+vue3+element-plus客户端后台系统EXE
如果想要了解更多详细介绍,可以去看看下面这篇文章。
Tauri2-Vite7Admin客户端管理后台|tauri2.9+vue3+element-plus后台系统
往期推荐
Tauri2.8+Vue3聊天系统|vite7+tauri2+element-plus客户端仿微信聊天程序
Electron38-Vue3OS客户端OS系统|vite7+electron38+arco桌面os后台管理
electron38-admin桌面端后台|Electron38+Vue3+ElementPlus管理系统
Electron38-Wechat电脑端聊天|vite7+electron38仿微信桌面端聊天系统
原创uniapp+vue3+deepseek+uv-ui跨端实战仿deepseek/豆包流式ai聊天对话助手。
vue3-webseek网页版AI问答|Vite6+DeepSeek+Arco流式ai聊天打字效果
最新版uni-app+vue3+uv-ui跨三端仿微信app聊天应用【h5+小程序+app端】
Flutter3-MacOS桌面OS系统|flutter3.32+window_manager客户端OS模板
最新研发flutter3.27+bitsdojo_window+getx客户端仿微信聊天Exe应用
最新版Flutter3.32+Dart3.8跨平台仿微信app聊天界面|朋友圈
最新版uniapp+vue3+uv-ui跨三端短视频+直播+聊天【H5+小程序+App端】
uniapp-vue3-os手机oa系统|uni-app+vue3跨三端os后台管理模板
Electron35-DeepSeek桌面端AI系统|vue3.5+electron+arco客户端ai模板
uniapp+vue3酒店预订|vite5+uniapp预约订房系统模板(h5+小程序+App端)
Tauri2.0+Vite5聊天室|vue3+tauri2+element-plus仿微信|tauri聊天应用
tauri2.0-admin桌面端后台系统|Tauri2+Vite5+ElementPlus管理后台EXE程序
vue3-tauri2-admin:最新研发vite7.1+tauri2.9+vue3 setup+pinia3+elementPlus跨平台电脑端中后台管理系统Exe模板。包含了表格、图表、表单、列表、编辑器、错误处理等模块。
使用技术
- 开发工具:VScode
- 跨平台框架:Tauri^2.9
- 前端技术框架:vite^7.1.12+vue^3.5.22+vue-router^4.6.3
- 组件库:element-plus^2.11.5
- 状态管理:pinia^3.0.3
- 国际化方案:vue-i18n^11.1.12
- 图表组件:echarts^6.0.0
- markdown编辑器:md-editor-v3^6.1.0
- 富文本编辑器:@vueup/vue-quill^1.2.0
- 模拟数据:mockjs^1.1.0
项目结构目录
使用最新跨平台技术tauri2.9+vue3搭建项目模板页面。
tauri2-vue3admin客户端后台系统已经更新到我的原创作品集。
tauri2.9+vue3+element-plus客户端后台系统EXE
如果想要了解更多详细介绍,可以去看看下面这篇文章。
Tauri2-Vite7Admin客户端管理后台|tauri2.9+vue3+element-plus后台系统
往期推荐
Tauri2.8+Vue3聊天系统|vite7+tauri2+element-plus客户端仿微信聊天程序
Electron38-Vue3OS客户端OS系统|vite7+electron38+arco桌面os后台管理
electron38-admin桌面端后台|Electron38+Vue3+ElementPlus管理系统
Electron38-Wechat电脑端聊天|vite7+electron38仿微信桌面端聊天系统
原创uniapp+vue3+deepseek+uv-ui跨端实战仿deepseek/豆包流式ai聊天对话助手。
vue3-webseek网页版AI问答|Vite6+DeepSeek+Arco流式ai聊天打字效果
最新版uni-app+vue3+uv-ui跨三端仿微信app聊天应用【h5+小程序+app端】
Flutter3-MacOS桌面OS系统|flutter3.32+window_manager客户端OS模板
最新研发flutter3.27+bitsdojo_window+getx客户端仿微信聊天Exe应用
最新版Flutter3.32+Dart3.8跨平台仿微信app聊天界面|朋友圈
最新版uniapp+vue3+uv-ui跨三端短视频+直播+聊天【H5+小程序+App端】
uniapp-vue3-os手机oa系统|uni-app+vue3跨三端os后台管理模板
Electron35-DeepSeek桌面端AI系统|vue3.5+electron+arco客户端ai模板
uniapp+vue3酒店预订|vite5+uniapp预约订房系统模板(h5+小程序+App端)
Tauri2.0+Vite5聊天室|vue3+tauri2+element-plus仿微信|tauri聊天应用
tauri2.0-admin桌面端后台系统|Tauri2+Vite5+ElementPlus管理后台EXE程序
【鸿蒙征文】记一次鸿蒙Next原生插件开发与Uniapp调用实战之弹层开发(tmui4x中的xToasts弹层)(含源码附件)
本文实现目标
- 掌握如何使用devEco-Studio6开发和创建一个Har插件
- 插件与unipp之间如何传参数并调用之
- 如何在ArkTs中绘制渲染自定义界面
- 如何在ArkTs中模块化界面组件
- 创建uniapp插件并调用自己写的鸿蒙原生插件
完成本次目标:全局弹层框
附件会有本次示例的源码包,可以直接使用,是tmui4x组件库的一部分。
本文阅读前的要求
需要你了解以下开发语言
- ArtTs,这块学习掌握应该很快,因为真的和TS像。
- UTS的了解,也是相对简单,参见Dcloud官方文档即可(在本文中占比较少,主要是着重如何利用它作为一个桥梁)
- uniAppx的环境搭建及devEco-staudio6的环境搭建。
如果你满足了以上要求可以按本文代码复制阅读可以直接运行自己的插件。
如果还未满足,可以当作是了解学习结构的一个文章知识。
一、测试的环境
- 电脑:mac mini2 ,macOs 26
- HbuilderX工具:4.84
- 鸿蒙开发工具:devEco-Studio 6,模拟器 HarmonyOs 6.0.0
二、创建Har
1、 创建Harmony测试项目
a. 如图创建鸿蒙项目
b. 继续
c. 点Finish
d. 首页页面代码
2、 创建Harmony的模拟器
- 接着上步,点击
No devices下拉框选中Devices Manager,打开 模拟器管理界面 - 选中菜单栏 Phone 后在界面的尾部,选中
New Emulator创建,记得下载鸿蒙6的依赖,等下载创建成功后即可。 - 运行模拟器
点击运行模拟器:
3、 运行鸿蒙应用
- 回到第一步,点运行按钮,此时旁边会有刚才创建和运行的模拟器啦 。
- 运行应用后,说明前期的鸿蒙开发基础工作完成了。
4、 创建Har包
如图创建一个module
创建成功后,会有一个目录结构如图:
至此Har包创建完成了
5、编写插件代码
我们继续刚才的插件,找到插件根目录下的index.ets文件如上图高亮。
代码内容如图(附件中提供):
6、核心要点传参
我们知道在uts中的有各种各样的类型。比如本插件中,中我为弹层组件编写了一个类型
export type XTOAST_TYPE = {
iconColor?:string,
contentBgColor?:string,
maskBgColor?:string,
iconSize?:number,
/**
* 正常应该填写图标的16进制符号如:EEC4
* 本组件允许几个状态名称使用默认图标,为空是info图标。
* warn,error,success,info
*/
iconCode?:string,
/**
* 标题为空不显示。
*/
title:string,
titleSize?:number,
titleColor?:string,
/**
* 几秒后消失,0 表示永不消失
*/
duration?:number,
close?:()=>void,
/** 宽 */
size?:number,
/**
* 禁用遮罩穿透点击事件
* @default true
*/
maskDisableClik?:boolean
}
上面的类型如何在Har插件中如何接受?
在ArtTs中,有一个类型是 ESObject,你可以理解为JS中的Object或者UTS中的UTSOBJect类型。
在Har中,只要写对应的参数类型为params: ESObject即可。
比如弹层中的代码:
/**
* 打开提示
* @param context UIContext
* @param params ESObject 参数
*/
export function xshowtoasts(context: UIContext, params: ESObject){
//...
}
a. 如何创建一个自定界面
@Builder
export function customBuilderModal(params: Params) {
Column({space:5}) {
if (params.context.iconCode != '') {
Text(String.fromCharCode(parseInt(params.context.iconCode, 16)))
.fontColor(params.context.iconColor)
.fontSize(params.context.iconSize)
.fontFamily("remixicon")
}
Text(`${params.context.title}`)
.fontSize(params.context.titleSize)
.fontColor(params.context.titleColor)
.textAlign(TextAlign.Center)
}
.padding(16)
.borderRadius(16)
.backgroundColor(params.context.contentBgColor)
.shadow({
offsetX: 0,
offsetY: 5,
radius: 20,
color: 'rgba(0,0,0,0.1)'
})
.justifyContent(FlexAlign.Center)
.constraintSize({
minWidth:params.context.iconCode==''?0:params.context.size,
minHeight:params.context.iconCode==''?0:params.context.size,
maxWidth:300
})
.clip(true)
}
b. 渲染自定界面
我们可以了解下wrapBuilder它可以将模块界面编译为一个渲染Node供界面上渲染。
代码中
let globalBuilder: WrappedBuilder<[ESObject]> = wrapBuilder(customBuilderModal);
const contentNode = new ComponentContent(context, globalBuilder, new Params(context, params))
7. 完成Har插件的编写
前面只是讲述源码的关键点,具体因为内容牵涉众中,请下载附件研究,我们主要是要了解让UTS与har之间调用。
在附件中下载我写的插件即可。
8. 编译Har插件为依赖包
找到依赖文件:
至此我们一写har插件完成啦。
三、创建UniApp sdk插件
1. 复制Har依赖
复制前面我们编译好的Har依赖包到鸿蒙sdk目录,
请一定按图所示创建目录层级,并把文件复制进来。
添加依赖
2. 编写UTS插件代码
如图打开index.uts
3. 核心要点
a. 如何获取鸿蒙所需要参数UIContext?
UTSHarmony.getCurrentWindow()!.getUIContext()即可
因为大部分代码我们在鸿蒙插件内完成,因此UTS这边只需要调用对应函数即可。无需过多东西。
四、运行UTS插件
在页面中导入即可使用
import { showToast,XTOAST_TYPE } from "@/uni_modules/x-toast-s"
onReady(()=>{
showToast({
title:"提示消息提示",
duration:1000*3,
close() {
console.log('close....')
}
})
})
五、运行Uniapp项目
运行你的uniapp项目就会在页面中显示一条全屏的弹层啦。
六、总结
上面的代码是粗略的讲述整个鸿蒙har插件创建,然后编译,再创建UTS创建,引用,调用har插件,
这是一个非常长的过程,中间详细的代码如何编写文章并未提及,因为牵涉过多,我们只讲述过程。
知识总结:
- 在鸿蒙原生插件里面学习如何结构化自定义界面
- 如何在鸿蒙原生插件中写全局弹层的界面
- 如何在UTS插件这边传递UIContext及获取,以及参数类型的对应
- 如何从0开始创建一个鸿蒙应用,及从0开始创建一个原生鸿蒙Har插件并编译。
七、源码声明
为了响应论坛的繁荣发展,因此源码及相关文件可以在附件中下载得到,
通过此文章,你可拓展更广的应用插件开发方向:
- 其它全局的插件如modal,tips,page等一切可覆盖全屏的页面组件等
- 鸿蒙原生插件开发的流程及要点。
本附件中的源码仅供学习参考,不作为最终生产用途的保证。
本文实现目标
- 掌握如何使用devEco-Studio6开发和创建一个Har插件
- 插件与unipp之间如何传参数并调用之
- 如何在ArkTs中绘制渲染自定义界面
- 如何在ArkTs中模块化界面组件
- 创建uniapp插件并调用自己写的鸿蒙原生插件
完成本次目标:全局弹层框
附件会有本次示例的源码包,可以直接使用,是tmui4x组件库的一部分。
本文阅读前的要求
需要你了解以下开发语言
- ArtTs,这块学习掌握应该很快,因为真的和TS像。
- UTS的了解,也是相对简单,参见Dcloud官方文档即可(在本文中占比较少,主要是着重如何利用它作为一个桥梁)
- uniAppx的环境搭建及devEco-staudio6的环境搭建。
如果你满足了以上要求可以按本文代码复制阅读可以直接运行自己的插件。
如果还未满足,可以当作是了解学习结构的一个文章知识。
一、测试的环境
- 电脑:mac mini2 ,macOs 26
- HbuilderX工具:4.84
- 鸿蒙开发工具:devEco-Studio 6,模拟器 HarmonyOs 6.0.0
二、创建Har
1、 创建Harmony测试项目
a. 如图创建鸿蒙项目
b. 继续
c. 点Finish
d. 首页页面代码
2、 创建Harmony的模拟器
- 接着上步,点击
No devices下拉框选中Devices Manager,打开 模拟器管理界面 - 选中菜单栏 Phone 后在界面的尾部,选中
New Emulator创建,记得下载鸿蒙6的依赖,等下载创建成功后即可。 - 运行模拟器
点击运行模拟器:
3、 运行鸿蒙应用
- 回到第一步,点运行按钮,此时旁边会有刚才创建和运行的模拟器啦 。
- 运行应用后,说明前期的鸿蒙开发基础工作完成了。
4、 创建Har包
如图创建一个module
创建成功后,会有一个目录结构如图:
至此Har包创建完成了
5、编写插件代码
我们继续刚才的插件,找到插件根目录下的index.ets文件如上图高亮。
代码内容如图(附件中提供):
6、核心要点传参
我们知道在uts中的有各种各样的类型。比如本插件中,中我为弹层组件编写了一个类型
export type XTOAST_TYPE = {
iconColor?:string,
contentBgColor?:string,
maskBgColor?:string,
iconSize?:number,
/**
* 正常应该填写图标的16进制符号如:EEC4
* 本组件允许几个状态名称使用默认图标,为空是info图标。
* warn,error,success,info
*/
iconCode?:string,
/**
* 标题为空不显示。
*/
title:string,
titleSize?:number,
titleColor?:string,
/**
* 几秒后消失,0 表示永不消失
*/
duration?:number,
close?:()=>void,
/** 宽 */
size?:number,
/**
* 禁用遮罩穿透点击事件
* @default true
*/
maskDisableClik?:boolean
}
上面的类型如何在Har插件中如何接受?
在ArtTs中,有一个类型是 ESObject,你可以理解为JS中的Object或者UTS中的UTSOBJect类型。
在Har中,只要写对应的参数类型为params: ESObject即可。
比如弹层中的代码:
/**
* 打开提示
* @param context UIContext
* @param params ESObject 参数
*/
export function xshowtoasts(context: UIContext, params: ESObject){
//...
}
a. 如何创建一个自定界面
@Builder
export function customBuilderModal(params: Params) {
Column({space:5}) {
if (params.context.iconCode != '') {
Text(String.fromCharCode(parseInt(params.context.iconCode, 16)))
.fontColor(params.context.iconColor)
.fontSize(params.context.iconSize)
.fontFamily("remixicon")
}
Text(`${params.context.title}`)
.fontSize(params.context.titleSize)
.fontColor(params.context.titleColor)
.textAlign(TextAlign.Center)
}
.padding(16)
.borderRadius(16)
.backgroundColor(params.context.contentBgColor)
.shadow({
offsetX: 0,
offsetY: 5,
radius: 20,
color: 'rgba(0,0,0,0.1)'
})
.justifyContent(FlexAlign.Center)
.constraintSize({
minWidth:params.context.iconCode==''?0:params.context.size,
minHeight:params.context.iconCode==''?0:params.context.size,
maxWidth:300
})
.clip(true)
}
b. 渲染自定界面
我们可以了解下wrapBuilder它可以将模块界面编译为一个渲染Node供界面上渲染。
代码中
let globalBuilder: WrappedBuilder<[ESObject]> = wrapBuilder(customBuilderModal);
const contentNode = new ComponentContent(context, globalBuilder, new Params(context, params))
7. 完成Har插件的编写
前面只是讲述源码的关键点,具体因为内容牵涉众中,请下载附件研究,我们主要是要了解让UTS与har之间调用。
在附件中下载我写的插件即可。
8. 编译Har插件为依赖包
找到依赖文件:
至此我们一写har插件完成啦。
三、创建UniApp sdk插件
1. 复制Har依赖
复制前面我们编译好的Har依赖包到鸿蒙sdk目录,
请一定按图所示创建目录层级,并把文件复制进来。
添加依赖
2. 编写UTS插件代码
如图打开index.uts
3. 核心要点
a. 如何获取鸿蒙所需要参数UIContext?
UTSHarmony.getCurrentWindow()!.getUIContext()即可
因为大部分代码我们在鸿蒙插件内完成,因此UTS这边只需要调用对应函数即可。无需过多东西。
四、运行UTS插件
在页面中导入即可使用
import { showToast,XTOAST_TYPE } from "@/uni_modules/x-toast-s"
onReady(()=>{
showToast({
title:"提示消息提示",
duration:1000*3,
close() {
console.log('close....')
}
})
})
五、运行Uniapp项目
运行你的uniapp项目就会在页面中显示一条全屏的弹层啦。
六、总结
上面的代码是粗略的讲述整个鸿蒙har插件创建,然后编译,再创建UTS创建,引用,调用har插件,
这是一个非常长的过程,中间详细的代码如何编写文章并未提及,因为牵涉过多,我们只讲述过程。
知识总结:
- 在鸿蒙原生插件里面学习如何结构化自定义界面
- 如何在鸿蒙原生插件中写全局弹层的界面
- 如何在UTS插件这边传递UIContext及获取,以及参数类型的对应
- 如何从0开始创建一个鸿蒙应用,及从0开始创建一个原生鸿蒙Har插件并编译。
七、源码声明
为了响应论坛的繁荣发展,因此源码及相关文件可以在附件中下载得到,
通过此文章,你可拓展更广的应用插件开发方向:
- 其它全局的插件如modal,tips,page等一切可覆盖全屏的页面组件等
- 鸿蒙原生插件开发的流程及要点。
本附件中的源码仅供学习参考,不作为最终生产用途的保证。
收起阅读 »【鸿蒙征文】从零实现 uni-app 鸿蒙平台 TTS 插件:UTS 开发实践指南
从零实现 uni-app 鸿蒙平台 TTS 插件:UTS 开发实践指南
随着移动应用的日益普及,文本转语音(TTS)功能已经成为提升用户体验的重要组成部分。在跨平台开发中,我们常常需要为不同平台提供一致的语音合成能力。
uni-app框架在安卓和IOS提供speech能力,但在鸿蒙(HarmonyOS)平台上,不提供speech支持。本文将详细介绍如何通过UTS技术开发一个鸿蒙平台的TTS插件,弥补这一功能空白,并学习如何实现跨平台接口的一致性。
一、项目概述
本项目将开发一个名为Lime TTS的基于UTS(Uni TypeScript)的文本转语音插件,主要目标是为uni-app在鸿蒙平台上提供语音合成能力。通过本项目的学习,我们将掌握如何使用UTS技术调用鸿蒙原生API,实现跨平台插件开发的核心技能。
二、项目目录结构
我们的Lime TTS项目采用标准的uni-app UTS插件结构,清晰地分离了接口定义和平台实现。这种结构设计有助于我们组织代码,并确保跨平台实现的一致性。项目的目录结构如下:
uni_modules/
└── lime-tts/
├── changelog.md
├── package.json
├── readme.md
└── utssdk/
├── app-android/
│ ├── config.json
│ └── index.uts
├── app-harmony/
│ ├── config.json
│ └── index.uts
├── app-ios/
│ ├── config.json
│ └── index.uts
├── interface.uts
├── unierror.uts
└── web/
└── index.uts
- utssdk/interface.uts:定义了插件的公共接口和类型,是插件的核心规范文件。
- utssdk/app-harmony/index.uts:鸿蒙平台的具体实现代码。
三、接口设计与定义
3.1 核心接口和类型设计
Lime TTS 插件通过 uni_modules/lime-tts/utssdk/interface.uts 文件定义了一套完整的类型和接口规范,确保了良好的类型安全性和代码提示。
语音合成选项
export type SpeakOptions = {
/**
* 语速
* 可选,支持范围 [0.5-2],默认值为 1
*/
speed ?: number;
/**
* 音量
* 可选,支持范围 [0-2],默认值为 1
*/
volume ?: number;
/**
* 音调
* 可选,支持范围 [0.5-2],默认值为 1
*/
pitch ?: number;
/**
* 语境,播放阿拉伯数字用的语种
* 可选,支持 "zh-CN" 中文与 "en-US" 英文,默认 "zh-CN"
*/
language ?: Language;
// 其他配置项...
}
音色信息
export type VoiceInfo = {
language : Language;
person : number;
name : string;
gender : 'male' | 'female';
description : string;
status : 'available' | 'downloadable' | 'unavailable';
}
语音状态
export type SpeechStatus = 'idle' | 'speaking' | 'paused' | 'uninitialized';
3.2 主要功能接口
export interface LimeTTS {
/**
* 朗读指定文本
* @param text 要朗读的文本
* @param options 可选参数,如语音ID、语速等
*/
speak(text : string) : void;
speak(text : string, options : SpeakOptions | null) : void;
/**
* 停止当前朗读
*/
stop() : void;
/**
* 暂停当前朗读
*/
pause() : void;
/**
* 获取可用语音列表
*/
getVoices() : Promise<VoiceInfo[]>;
/**
* 获取当前语音状态
*/
getStatus() : SpeechStatus;
/**
* 销毁TTS实例,释放资源
*/
destroy() : void;
/**
* 监听TTS事件
* @param event 事件类型
* @param callback 回调函数
*/
on(event : 'start' | 'end' | 'error' | 'stop', callback : SpeechCallback) : void;
off(event : 'start' | 'end' | 'error' | 'stop') : void;
off(event : 'start' | 'end' | 'error' | 'stop', callback : SpeechCallback | null) : void;
}
四、鸿蒙平台实现详解
在本节中,我们将详细学习如何为鸿蒙平台实现TTS功能。我们将通过调用鸿蒙原生的@kit.CoreSpeechKit来实现语音合成,并遵循之前设计的接口规范。
4.0 模块导入
在鸿蒙平台实现中,首先需要导入鸿蒙系统提供的核心语音合成模块:
// 导入鸿蒙语音合成相关模块
import { textToSpeech } from '@kit.CoreSpeechKit';
import { BusinessError } from '@kit.BasicServicesKit';
// 导入公共接口定义
import { LimeTTS, SpeakOptions, VoiceInfo, SpeechStatus, SpeechCallback } from '../interface.uts';
4.1 核心实现类
在uni_modules/lime-tts/utssdk/app-harmony/index.uts文件中,定义了LimeTTSImpl类,该类是鸿蒙平台上TTS功能的核心实现:
class LimeTTSImpl implements LimeTTS {
private engine : textToSpeech.TextToSpeechEngine | null = null;
private currentVoice : textToSpeech.CreateEngineParams | null = null;
private eventListeners : Map<string, Array<SpeechCallback>> = new Map<string, Array<SpeechCallback>>();
private isSpeaking : boolean = false;
private isPaused : boolean = false;
// ...
}
4.2 引擎初始化
在构造函数中,插件会初始化 TTS 引擎并设置相关回调:
private initialize(options : CreateEngineParams | null) {
// 清理旧引擎
if (this.engine) {
this.engine.shutdown();
}
const params : textToSpeech.CreateEngineParams = {
language: options?.language ?? 'zh-CN',
person: options?.person ?? 0,
online: 1
}
this.currentVoice = params
const _this = this
textToSpeech.createEngine(params).then((res : textToSpeech.TextToSpeechEngine) => {
this.engine = res
// 设置speak的回调信息
let speakListener : textToSpeech.SpeakListener = {
// 开始播报回调
onStart(requestId : string, response : textToSpeech.StartResponse) {
_this.isSpeaking = true;
_this.isPaused = false;
_this.emit('start', { type: 'start' })
},
// 其他回调...
};
this.engine.setListener(speakListener);
}).catch((err : BusinessError) => {
console.error(`Failed to create engine. Code: ${err.code}, message: ${err.message}.`);
});
}
4.3 文本朗读功能
speak(text : string) : void
speak(text : string, options : SpeakOptions | null = null) {
const extraParams : ESObject = {
speed: options?.speed ?? 1,
volume: options?.volume ?? 1,
pitch: options?.pitch ?? 1,
languageContext: options?.language ?? 'zh-CN'
} as ESObject
this.engine?.speak(text, {
requestId: this.generateRequestId(),
extraParams
} as textToSpeech.SpeakParams);
}
4.4 音色管理
插件实现了音色列表获取和映射功能,将鸿蒙系统的原生音色信息转换为统一格式:
async getVoices() : Promise<VoiceInfo[]> {
try {
if (this.engine) {
// 使用引擎实例查询
const queryParams : textToSpeech.VoiceQuery = {
requestId: this.generateRequestId('voice_query'),
online: 1
};
const voices = await this.engine.listVoices(queryParams);
return this.mapToVoiceInfo(voices);
} else {
// 使用全局方法查询
const queryParams : textToSpeech.VoiceQuery = {
requestId: this.generateRequestId('voice_query'),
online: 1
};
const voices = await textToSpeech.listVoices(queryParams);
return this.mapToVoiceInfo(voices);
}
} catch (error) {
console.error('Failed to get voices:', error);
return Promise.resolve([])
}
}
private mapToVoiceInfo(voices : textToSpeech.VoiceInfo[]) : VoiceInfo[] {
return voices.map(voice => {
// 根据 person 值映射音色名称
let name = 'Unknown';
let gender : 'male' | 'female' = 'female';
switch (voice.person) {
case 0:
case 13:
name = '聆小珊';
gender = 'female';
break;
case 21:
name = '凌飞哲';
gender = 'male';
break;
case 8:
name = 'Laura';
gender = 'female';
break;
}
return {
language: voice.language.replace('_', '-'),
person: voice.person,
name: name,
gender: gender,
description: voice.description || `${name} ${gender === 'male' ? '男声' : '女声'}`,
status: voice.status === 'INSTALLED' ? 'available' :
voice.status === 'GA' ? 'downloadable' : 'unavailable'
} as VoiceInfo;
});
}
五、测试与验证
在开发完成插件后,我们需要编写测试代码来验证功能是否正常工作。以下是一个用于测试Lime TTS插件的代码示例:
// 导入我们开发的 TTS 工具函数
import { useTTS } from '@/uni_modules/lime-tts'
// 创建 TTS 实例 - 测试实例化功能
const tts = useTTS();
// 设置事件监听 - 测试事件机制
// 播放开始事件
const onStart = (event) => {
console.log('语音播放开始');
};
// 播放结束事件
const onEnd = (event) => {
console.log('语音播放结束');
};
// 错误处理事件
const onError = (event) => {
console.error('语音播放错误:', event);
};
// 注册事件监听器
tts.on('start', onStart);
tts.on('end', onEnd);
tts.on('error', onError);
// 测试核心功能 - 文本朗读
tts.speak('欢迎使用 Lime TTS 插件!', {
speed: 1.0, // 语速
volume: 1.0, // 音量
pitch: 1.0 // 音调
});
// 测试控制功能
setTimeout(() => {
// 测试暂停功能
tts.pause();
}, 3000);
// 测试状态查询功能
const status = tts.getStatus();
console.log('当前语音状态:', status);
// 测试音色管理功能
tts.getVoices().then(voices => {
console.log('可用音色列表:', voices);
});
// 测试资源释放 - 在组件卸载时调用
const cleanup = () => {
// 移除事件监听器
tts.off('start', onStart);
tts.off('end', onEnd);
tts.off('error', onError);
// 销毁实例,释放系统资源
tts.destroy();
};
六、鸿蒙平台特别说明
在鸿蒙平台上,Lime TTS 插件支持以下音色:
- 中文女声:聆小珊(person: 13,推荐使用)
- 中文男声:凌飞哲(person: 21,需要下载)
- 英文女声:Laura(person: 8,需要下载)
七、总结
通过本文的学习,我们详细了解了如何使用UTS技术为uni-app开发鸿蒙平台的TTS插件。这个过程涵盖了从接口定义、模块导入到核心功能实现的完整开发流程,展示了如何通过调用鸿蒙原生的@kit.CoreSpeechKit能力,弥补uni-app在鸿蒙平台上speech API缺失的问题。
开发鸿蒙平台插件的关键要点包括:
- 设计统一的跨平台接口,确保API一致性
- 正确导入鸿蒙系统模块,如
@kit.CoreSpeechKit和@kit.BasicServicesKit - 实现核心功能类,处理平台特定的API细节
- 封装事件监听机制,提供良好的开发者体验
- 处理音色映射和状态管理,确保功能完整性
希望本文能为您学习如何开发uni-app鸿蒙插件提供有价值的参考,帮助您掌握UTS开发技术,为uni-app生态贡献更多优质的平台适配插件。
目前,本插件已上传至uni-app插件市场,全面支持uni-app和uni-appx框架,并兼容安卓、鸿蒙和Web三大平台,iOS平台将在稍晚上线。开发者可以直接在项目中集成使用,体验跨平台的TTS功能。lime-tts
从零实现 uni-app 鸿蒙平台 TTS 插件:UTS 开发实践指南
随着移动应用的日益普及,文本转语音(TTS)功能已经成为提升用户体验的重要组成部分。在跨平台开发中,我们常常需要为不同平台提供一致的语音合成能力。
uni-app框架在安卓和IOS提供speech能力,但在鸿蒙(HarmonyOS)平台上,不提供speech支持。本文将详细介绍如何通过UTS技术开发一个鸿蒙平台的TTS插件,弥补这一功能空白,并学习如何实现跨平台接口的一致性。
一、项目概述
本项目将开发一个名为Lime TTS的基于UTS(Uni TypeScript)的文本转语音插件,主要目标是为uni-app在鸿蒙平台上提供语音合成能力。通过本项目的学习,我们将掌握如何使用UTS技术调用鸿蒙原生API,实现跨平台插件开发的核心技能。
二、项目目录结构
我们的Lime TTS项目采用标准的uni-app UTS插件结构,清晰地分离了接口定义和平台实现。这种结构设计有助于我们组织代码,并确保跨平台实现的一致性。项目的目录结构如下:
uni_modules/
└── lime-tts/
├── changelog.md
├── package.json
├── readme.md
└── utssdk/
├── app-android/
│ ├── config.json
│ └── index.uts
├── app-harmony/
│ ├── config.json
│ └── index.uts
├── app-ios/
│ ├── config.json
│ └── index.uts
├── interface.uts
├── unierror.uts
└── web/
└── index.uts
- utssdk/interface.uts:定义了插件的公共接口和类型,是插件的核心规范文件。
- utssdk/app-harmony/index.uts:鸿蒙平台的具体实现代码。
三、接口设计与定义
3.1 核心接口和类型设计
Lime TTS 插件通过 uni_modules/lime-tts/utssdk/interface.uts 文件定义了一套完整的类型和接口规范,确保了良好的类型安全性和代码提示。
语音合成选项
export type SpeakOptions = {
/**
* 语速
* 可选,支持范围 [0.5-2],默认值为 1
*/
speed ?: number;
/**
* 音量
* 可选,支持范围 [0-2],默认值为 1
*/
volume ?: number;
/**
* 音调
* 可选,支持范围 [0.5-2],默认值为 1
*/
pitch ?: number;
/**
* 语境,播放阿拉伯数字用的语种
* 可选,支持 "zh-CN" 中文与 "en-US" 英文,默认 "zh-CN"
*/
language ?: Language;
// 其他配置项...
}
音色信息
export type VoiceInfo = {
language : Language;
person : number;
name : string;
gender : 'male' | 'female';
description : string;
status : 'available' | 'downloadable' | 'unavailable';
}
语音状态
export type SpeechStatus = 'idle' | 'speaking' | 'paused' | 'uninitialized';
3.2 主要功能接口
export interface LimeTTS {
/**
* 朗读指定文本
* @param text 要朗读的文本
* @param options 可选参数,如语音ID、语速等
*/
speak(text : string) : void;
speak(text : string, options : SpeakOptions | null) : void;
/**
* 停止当前朗读
*/
stop() : void;
/**
* 暂停当前朗读
*/
pause() : void;
/**
* 获取可用语音列表
*/
getVoices() : Promise<VoiceInfo[]>;
/**
* 获取当前语音状态
*/
getStatus() : SpeechStatus;
/**
* 销毁TTS实例,释放资源
*/
destroy() : void;
/**
* 监听TTS事件
* @param event 事件类型
* @param callback 回调函数
*/
on(event : 'start' | 'end' | 'error' | 'stop', callback : SpeechCallback) : void;
off(event : 'start' | 'end' | 'error' | 'stop') : void;
off(event : 'start' | 'end' | 'error' | 'stop', callback : SpeechCallback | null) : void;
}
四、鸿蒙平台实现详解
在本节中,我们将详细学习如何为鸿蒙平台实现TTS功能。我们将通过调用鸿蒙原生的@kit.CoreSpeechKit来实现语音合成,并遵循之前设计的接口规范。
4.0 模块导入
在鸿蒙平台实现中,首先需要导入鸿蒙系统提供的核心语音合成模块:
// 导入鸿蒙语音合成相关模块
import { textToSpeech } from '@kit.CoreSpeechKit';
import { BusinessError } from '@kit.BasicServicesKit';
// 导入公共接口定义
import { LimeTTS, SpeakOptions, VoiceInfo, SpeechStatus, SpeechCallback } from '../interface.uts';
4.1 核心实现类
在uni_modules/lime-tts/utssdk/app-harmony/index.uts文件中,定义了LimeTTSImpl类,该类是鸿蒙平台上TTS功能的核心实现:
class LimeTTSImpl implements LimeTTS {
private engine : textToSpeech.TextToSpeechEngine | null = null;
private currentVoice : textToSpeech.CreateEngineParams | null = null;
private eventListeners : Map<string, Array<SpeechCallback>> = new Map<string, Array<SpeechCallback>>();
private isSpeaking : boolean = false;
private isPaused : boolean = false;
// ...
}
4.2 引擎初始化
在构造函数中,插件会初始化 TTS 引擎并设置相关回调:
private initialize(options : CreateEngineParams | null) {
// 清理旧引擎
if (this.engine) {
this.engine.shutdown();
}
const params : textToSpeech.CreateEngineParams = {
language: options?.language ?? 'zh-CN',
person: options?.person ?? 0,
online: 1
}
this.currentVoice = params
const _this = this
textToSpeech.createEngine(params).then((res : textToSpeech.TextToSpeechEngine) => {
this.engine = res
// 设置speak的回调信息
let speakListener : textToSpeech.SpeakListener = {
// 开始播报回调
onStart(requestId : string, response : textToSpeech.StartResponse) {
_this.isSpeaking = true;
_this.isPaused = false;
_this.emit('start', { type: 'start' })
},
// 其他回调...
};
this.engine.setListener(speakListener);
}).catch((err : BusinessError) => {
console.error(`Failed to create engine. Code: ${err.code}, message: ${err.message}.`);
});
}
4.3 文本朗读功能
speak(text : string) : void
speak(text : string, options : SpeakOptions | null = null) {
const extraParams : ESObject = {
speed: options?.speed ?? 1,
volume: options?.volume ?? 1,
pitch: options?.pitch ?? 1,
languageContext: options?.language ?? 'zh-CN'
} as ESObject
this.engine?.speak(text, {
requestId: this.generateRequestId(),
extraParams
} as textToSpeech.SpeakParams);
}
4.4 音色管理
插件实现了音色列表获取和映射功能,将鸿蒙系统的原生音色信息转换为统一格式:
async getVoices() : Promise<VoiceInfo[]> {
try {
if (this.engine) {
// 使用引擎实例查询
const queryParams : textToSpeech.VoiceQuery = {
requestId: this.generateRequestId('voice_query'),
online: 1
};
const voices = await this.engine.listVoices(queryParams);
return this.mapToVoiceInfo(voices);
} else {
// 使用全局方法查询
const queryParams : textToSpeech.VoiceQuery = {
requestId: this.generateRequestId('voice_query'),
online: 1
};
const voices = await textToSpeech.listVoices(queryParams);
return this.mapToVoiceInfo(voices);
}
} catch (error) {
console.error('Failed to get voices:', error);
return Promise.resolve([])
}
}
private mapToVoiceInfo(voices : textToSpeech.VoiceInfo[]) : VoiceInfo[] {
return voices.map(voice => {
// 根据 person 值映射音色名称
let name = 'Unknown';
let gender : 'male' | 'female' = 'female';
switch (voice.person) {
case 0:
case 13:
name = '聆小珊';
gender = 'female';
break;
case 21:
name = '凌飞哲';
gender = 'male';
break;
case 8:
name = 'Laura';
gender = 'female';
break;
}
return {
language: voice.language.replace('_', '-'),
person: voice.person,
name: name,
gender: gender,
description: voice.description || `${name} ${gender === 'male' ? '男声' : '女声'}`,
status: voice.status === 'INSTALLED' ? 'available' :
voice.status === 'GA' ? 'downloadable' : 'unavailable'
} as VoiceInfo;
});
}
五、测试与验证
在开发完成插件后,我们需要编写测试代码来验证功能是否正常工作。以下是一个用于测试Lime TTS插件的代码示例:
// 导入我们开发的 TTS 工具函数
import { useTTS } from '@/uni_modules/lime-tts'
// 创建 TTS 实例 - 测试实例化功能
const tts = useTTS();
// 设置事件监听 - 测试事件机制
// 播放开始事件
const onStart = (event) => {
console.log('语音播放开始');
};
// 播放结束事件
const onEnd = (event) => {
console.log('语音播放结束');
};
// 错误处理事件
const onError = (event) => {
console.error('语音播放错误:', event);
};
// 注册事件监听器
tts.on('start', onStart);
tts.on('end', onEnd);
tts.on('error', onError);
// 测试核心功能 - 文本朗读
tts.speak('欢迎使用 Lime TTS 插件!', {
speed: 1.0, // 语速
volume: 1.0, // 音量
pitch: 1.0 // 音调
});
// 测试控制功能
setTimeout(() => {
// 测试暂停功能
tts.pause();
}, 3000);
// 测试状态查询功能
const status = tts.getStatus();
console.log('当前语音状态:', status);
// 测试音色管理功能
tts.getVoices().then(voices => {
console.log('可用音色列表:', voices);
});
// 测试资源释放 - 在组件卸载时调用
const cleanup = () => {
// 移除事件监听器
tts.off('start', onStart);
tts.off('end', onEnd);
tts.off('error', onError);
// 销毁实例,释放系统资源
tts.destroy();
};
六、鸿蒙平台特别说明
在鸿蒙平台上,Lime TTS 插件支持以下音色:
- 中文女声:聆小珊(person: 13,推荐使用)
- 中文男声:凌飞哲(person: 21,需要下载)
- 英文女声:Laura(person: 8,需要下载)
七、总结
通过本文的学习,我们详细了解了如何使用UTS技术为uni-app开发鸿蒙平台的TTS插件。这个过程涵盖了从接口定义、模块导入到核心功能实现的完整开发流程,展示了如何通过调用鸿蒙原生的@kit.CoreSpeechKit能力,弥补uni-app在鸿蒙平台上speech API缺失的问题。
开发鸿蒙平台插件的关键要点包括:
- 设计统一的跨平台接口,确保API一致性
- 正确导入鸿蒙系统模块,如
@kit.CoreSpeechKit和@kit.BasicServicesKit - 实现核心功能类,处理平台特定的API细节
- 封装事件监听机制,提供良好的开发者体验
- 处理音色映射和状态管理,确保功能完整性
希望本文能为您学习如何开发uni-app鸿蒙插件提供有价值的参考,帮助您掌握UTS开发技术,为uni-app生态贡献更多优质的平台适配插件。
目前,本插件已上传至uni-app插件市场,全面支持uni-app和uni-appx框架,并兼容安卓、鸿蒙和Web三大平台,iOS平台将在稍晚上线。开发者可以直接在项目中集成使用,体验跨平台的TTS功能。lime-tts
收起阅读 »【鸿蒙征文】关于鸿蒙折叠屏(宽屏设备)适配的一些分享
随着鸿蒙系统的不断发展以及uni-app x 对鸿蒙的全面支持,相信使用 uni-app X 进行鸿蒙应用及跨平台应用开发,是前端开发的最优选择。近期完成了对公司的已有应用进行鸿蒙平台适配的适配工作,有了 uni-app X 为基础,整个过程变得“轻松、高效”!
在折叠屏的适配过程中获得了一些经验,分享给大家(仅是自己的一些想法,欢迎大家指正~)。
适配目标效果
如上图所示:屏幕展开时(宽屏),卡片为一行两列布局,屏幕折叠时(窄屏)变成一行一个卡片。
原理分享
1 在页面初始化时获取屏幕宽度,识别运行屏幕环境是否为宽屏;
2 在屏幕宽度变化时,重新判处屏幕运行环境;
3 创建对应的 flex 布局样式,使用 :class="" 进行动态样式切换,达到宽屏及折叠屏幕折叠、展开的适配;
源码分享
<template>
<view :class="['flex', isTable?'rows':'column', isTable?'flex-wrap':'']">
<view
v-for="(item, idx) in 10" :key="idx"
:class="['item', isTable ? 'w-half':'w-full']" >
<text class="text">{{idx}}</text>
</view>
</view>
</template>
<script>
export default {
data() {
return {
title: 'Hello',
windowWidth : 350, // 保留变量,可以记录屏幕宽度,单位 px
isTable : false,
}
},
onReady() {
//
let info = uni.getWindowInfo();
this.isTable = info.windowWidth > 400;
console.log(this.isTable);
},
methods: {
},
onResize : function(e){
this.isTable = e.size.windowWidth > 400;
console.log(this.isTable);
}
}
</script>
<style>
.flex{display:flex;}
.rows{flex-direction:row;}
.column{flex-direction:column;}
.flex-wrap{flex-wrap:wrap;}
.item{background-color:#FF0036;}
.w-full{width:700rpx; height:300rpx; margin:25rpx;}
.w-half{width:48%; height:200rpx; margin:20rpx 1%;}
.text{font-size:20px;}
</style>
总结
1 演示代码通过监听屏幕尺寸变化,使用 isTable 变量记录了屏幕是否为宽屏,然后通过 flex 布局切换,实现了布局目标;
2 演示代码虽然简单,经过自己的布局设计,完全可以适配 宽屏、折叠屏(动态监听折叠事件);
3 如果您在宽屏应用中遇到字体尺寸问题,可以以 text 组件为基础封装一个自己的文本组件,针对不同屏幕尺寸设置不同的字体大小(可以将 isTable 作为属性传递到组件内,通过 watch 观察屏幕变化,动态切换字体尺寸);
4 如果担心 onResize 监听多次触发引起的效率问题,可以使用 setTimeout 函数实现防抖,仅执行短时间内的一次变化即可;
左右功能不同的布局
如果您开发的应用在宽屏模式下,左侧为列表,右侧为详情:
您可以以 isTable 为核心,使用 if 进行右侧功能的条件渲染,如 : 宽屏模式
<view v-if="isTable">右侧功能区</view>
当我们点击列表项目时调用对应函数,同样判断是否宽屏模式,如果是再右侧详情区域展示详情,反之,打开一个新的页面展示详情即可。
一个很基础的原理分享,希望对大家的鸿蒙之旅有所帮助,谢谢阅读。
随着鸿蒙系统的不断发展以及uni-app x 对鸿蒙的全面支持,相信使用 uni-app X 进行鸿蒙应用及跨平台应用开发,是前端开发的最优选择。近期完成了对公司的已有应用进行鸿蒙平台适配的适配工作,有了 uni-app X 为基础,整个过程变得“轻松、高效”!
在折叠屏的适配过程中获得了一些经验,分享给大家(仅是自己的一些想法,欢迎大家指正~)。
适配目标效果
如上图所示:屏幕展开时(宽屏),卡片为一行两列布局,屏幕折叠时(窄屏)变成一行一个卡片。
原理分享
1 在页面初始化时获取屏幕宽度,识别运行屏幕环境是否为宽屏;
2 在屏幕宽度变化时,重新判处屏幕运行环境;
3 创建对应的 flex 布局样式,使用 :class="" 进行动态样式切换,达到宽屏及折叠屏幕折叠、展开的适配;
源码分享
<template>
<view :class="['flex', isTable?'rows':'column', isTable?'flex-wrap':'']">
<view
v-for="(item, idx) in 10" :key="idx"
:class="['item', isTable ? 'w-half':'w-full']" >
<text class="text">{{idx}}</text>
</view>
</view>
</template>
<script>
export default {
data() {
return {
title: 'Hello',
windowWidth : 350, // 保留变量,可以记录屏幕宽度,单位 px
isTable : false,
}
},
onReady() {
//
let info = uni.getWindowInfo();
this.isTable = info.windowWidth > 400;
console.log(this.isTable);
},
methods: {
},
onResize : function(e){
this.isTable = e.size.windowWidth > 400;
console.log(this.isTable);
}
}
</script>
<style>
.flex{display:flex;}
.rows{flex-direction:row;}
.column{flex-direction:column;}
.flex-wrap{flex-wrap:wrap;}
.item{background-color:#FF0036;}
.w-full{width:700rpx; height:300rpx; margin:25rpx;}
.w-half{width:48%; height:200rpx; margin:20rpx 1%;}
.text{font-size:20px;}
</style>
总结
1 演示代码通过监听屏幕尺寸变化,使用 isTable 变量记录了屏幕是否为宽屏,然后通过 flex 布局切换,实现了布局目标;
2 演示代码虽然简单,经过自己的布局设计,完全可以适配 宽屏、折叠屏(动态监听折叠事件);
3 如果您在宽屏应用中遇到字体尺寸问题,可以以 text 组件为基础封装一个自己的文本组件,针对不同屏幕尺寸设置不同的字体大小(可以将 isTable 作为属性传递到组件内,通过 watch 观察屏幕变化,动态切换字体尺寸);
4 如果担心 onResize 监听多次触发引起的效率问题,可以使用 setTimeout 函数实现防抖,仅执行短时间内的一次变化即可;
左右功能不同的布局
如果您开发的应用在宽屏模式下,左侧为列表,右侧为详情:
您可以以 isTable 为核心,使用 if 进行右侧功能的条件渲染,如 : 宽屏模式
<view v-if="isTable">右侧功能区</view>
当我们点击列表项目时调用对应函数,同样判断是否宽屏模式,如果是再右侧详情区域展示详情,反之,打开一个新的页面展示详情即可。
一个很基础的原理分享,希望对大家的鸿蒙之旅有所帮助,谢谢阅读。
收起阅读 »uni-app 也能开发纯血鸿蒙 App?使用 wot-starter 这样快速上手!
大家好,我是不如摸鱼去,wot-ui 的“主理人”,欢迎来到我的 uni-app 分享专栏。
wot-ui 是当下最流行的 uni-app vue3 组件库之一。
2024年,华为原生鸿蒙 HarmonyOS NEXT(5.0)正式发布,随后 uni-app 宣布了对原生鸿蒙的支持。如今 HarmonyOS 6 都发布了,uni-app 对纯血鸿蒙的支持如何了?我们今天来探索使用 wot-starter 构建支持 HarmonyOS NEXT 的 App。
项目环境
我选择使用我们团队维护的 wot-starter 作为起手项目,VSCode 作为主要开发工具,HbuilderX 和 DevEco Studio 作为调试和打包工具。
技术栈:
- wot-starter: 基于 vitesse-uni-app 深度整合 Wot UI 组件库的快速启动模板 https://starter.wot-ui.cn/
开发工具:
- HbuilderX: uni-app 专属开发工具 https://www.dcloud.io/hbuilderx.html
- DevEco Studio: 开发 HarmonyOS 应用及元服务的集成开发环境(IDE)https://developer.huawei.com/consumer/cn/deveco-studio/
- VSCode: 大家的最爱 https://code.visualstudio.com/
环境信息:
- node.js v22.17.1
所有的开发工具我们可以安装最新的版本。
运行
如果手上有 HarmonyOS NEXT 系统的手机可以将手机的开发者模式打开连接到电脑上,如果没有则可以安装鸿蒙模拟器,参考文档 https://uniapp.dcloud.net.cn/tutorial/harmony/runbuild.html#connectvirtually 进行安装启动
配置 DevEco-Studio 路径
配置调试证书
运行到手机
选择运行设备 or 模拟器后点击运行,在编译代码构建运行包的时候,有三个缓存使用策略可供选择:
- 根据变化差量更新缓存:正常使用缓存来避免重复操作,提高构建效率。
- 强制使用缓存,跳过编译:如果没有修改代码,只想重新运行起来,则可以使用这种方式,此时若 HBuilderX 检查到已经有构建好的运行包存在,则直接安装运行,否则按正常方式构建再运行。
- 清空缓存:每次升级 HBuilderX 之后,新旧版本的鸿蒙工程目录可能不完全兼容,为避免旧版本的干扰,首次运行的时候可以选择这个选项。 另外,如果运行时出现结果不符合预期的奇怪情况,可以尝试使用这种方式重新构建运行,以消除缓存错乱带来的干扰。
打包
创建App
我们访问https://developer.huawei.com/consumer/cn/service/josp/agc/index.html#/harmonyOSDevPlatform/172249065903274453,注册账号并新建一个 APP ID,这一步我们在申请调试证书时应该已经做过。
申请发布证书
参考教程 https://developer.huawei.com/consumer/cn/doc/app/agc-help-add-releasecert-0000001946273961 申请发布证书并保存下来。
生成证书请求文件
申请发布证书时需要证书请求文件,我们参考教程 https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ide-signing#section462703710326 在 DevEco Studio 中生成证书请求文件并保存。
注意填写的内容和生成的证书文件都需要妥善保管
配置打包证书
我们在 hbuilderx 中配置打包证书,参考教程:https://uniapp.dcloud.net.cn/tutorial/harmony/runbuild.html#signing-configs
打包
按照下图操作即可打包出 app 文件。
运行效果
运行效果如下图,与 iOS、安卓应用基本一致。
注意点
在运行到原生鸿蒙的过程中,有一些点需要注意下:
- harmony os 条件编译不可以使用
APP-PLUS,可以使用APP - plus API 大部分不可用
- manifest.json 中配置的证书信息,主要是 app-harmony 对应的内容要手动同步到
manifest.config.ts下,不然下次运行就没了。 - 自行探索...
CI/CD
目前是依赖 HbuilderX 和 DevEco Studio 做 APP 构建的,HbuilderX CLI 也可以打包鸿蒙应用,我期望可以脱离 HbuilderX,使用 app-harmony 产物加 DevEco Studio 做 CI/CD,类似原来安卓离线打包一样的流程,目前还在探索中。
总结
我们今天验证了 通过 wot-starter 模板开发纯血鸿蒙(HarmonyOS NEXT)应用。技术栈基于 wot-ui 组件库与 vitesse-uni-app,结合VSCode、HBuilderX及DevEco Studio实现开发调试。流程涵盖环境配置、证书申请、真机/模拟器运行及打包发布,运行效果与安卓/iOS一致。需注意:条件编译禁用APP-PLUS(改用APP),plus API大部分不可用。当前构建依赖HBuilderX,未来探索通过app-harmony产物结合DevEco Studio实现CI/CD,为跨平台开发提供新路径。
参考资源
- wot-ui: https://wot-ui.cn/
- DevEco Studio: https://developer.huawei.com/consumer/cn/deveco-studio/
- wot-starter: https://starter.wot-ui.cn/
- uni-app 鸿蒙教程: https://uniapp.dcloud.net.cn/tutorial/harmony/runbuild.html
欢迎评论区沟通、讨论👇👇
大家好,我是不如摸鱼去,wot-ui 的“主理人”,欢迎来到我的 uni-app 分享专栏。
wot-ui 是当下最流行的 uni-app vue3 组件库之一。
2024年,华为原生鸿蒙 HarmonyOS NEXT(5.0)正式发布,随后 uni-app 宣布了对原生鸿蒙的支持。如今 HarmonyOS 6 都发布了,uni-app 对纯血鸿蒙的支持如何了?我们今天来探索使用 wot-starter 构建支持 HarmonyOS NEXT 的 App。
项目环境
我选择使用我们团队维护的 wot-starter 作为起手项目,VSCode 作为主要开发工具,HbuilderX 和 DevEco Studio 作为调试和打包工具。
技术栈:
- wot-starter: 基于 vitesse-uni-app 深度整合 Wot UI 组件库的快速启动模板 https://starter.wot-ui.cn/
开发工具:
- HbuilderX: uni-app 专属开发工具 https://www.dcloud.io/hbuilderx.html
- DevEco Studio: 开发 HarmonyOS 应用及元服务的集成开发环境(IDE)https://developer.huawei.com/consumer/cn/deveco-studio/
- VSCode: 大家的最爱 https://code.visualstudio.com/
环境信息:
- node.js v22.17.1
所有的开发工具我们可以安装最新的版本。
运行
如果手上有 HarmonyOS NEXT 系统的手机可以将手机的开发者模式打开连接到电脑上,如果没有则可以安装鸿蒙模拟器,参考文档 https://uniapp.dcloud.net.cn/tutorial/harmony/runbuild.html#connectvirtually 进行安装启动
配置 DevEco-Studio 路径
配置调试证书
运行到手机
选择运行设备 or 模拟器后点击运行,在编译代码构建运行包的时候,有三个缓存使用策略可供选择:
- 根据变化差量更新缓存:正常使用缓存来避免重复操作,提高构建效率。
- 强制使用缓存,跳过编译:如果没有修改代码,只想重新运行起来,则可以使用这种方式,此时若 HBuilderX 检查到已经有构建好的运行包存在,则直接安装运行,否则按正常方式构建再运行。
- 清空缓存:每次升级 HBuilderX 之后,新旧版本的鸿蒙工程目录可能不完全兼容,为避免旧版本的干扰,首次运行的时候可以选择这个选项。 另外,如果运行时出现结果不符合预期的奇怪情况,可以尝试使用这种方式重新构建运行,以消除缓存错乱带来的干扰。
打包
创建App
我们访问https://developer.huawei.com/consumer/cn/service/josp/agc/index.html#/harmonyOSDevPlatform/172249065903274453,注册账号并新建一个 APP ID,这一步我们在申请调试证书时应该已经做过。
申请发布证书
参考教程 https://developer.huawei.com/consumer/cn/doc/app/agc-help-add-releasecert-0000001946273961 申请发布证书并保存下来。
生成证书请求文件
申请发布证书时需要证书请求文件,我们参考教程 https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ide-signing#section462703710326 在 DevEco Studio 中生成证书请求文件并保存。
注意填写的内容和生成的证书文件都需要妥善保管
配置打包证书
我们在 hbuilderx 中配置打包证书,参考教程:https://uniapp.dcloud.net.cn/tutorial/harmony/runbuild.html#signing-configs
打包
按照下图操作即可打包出 app 文件。
运行效果
运行效果如下图,与 iOS、安卓应用基本一致。
注意点
在运行到原生鸿蒙的过程中,有一些点需要注意下:
- harmony os 条件编译不可以使用
APP-PLUS,可以使用APP - plus API 大部分不可用
- manifest.json 中配置的证书信息,主要是 app-harmony 对应的内容要手动同步到
manifest.config.ts下,不然下次运行就没了。 - 自行探索...
CI/CD
目前是依赖 HbuilderX 和 DevEco Studio 做 APP 构建的,HbuilderX CLI 也可以打包鸿蒙应用,我期望可以脱离 HbuilderX,使用 app-harmony 产物加 DevEco Studio 做 CI/CD,类似原来安卓离线打包一样的流程,目前还在探索中。
总结
我们今天验证了 通过 wot-starter 模板开发纯血鸿蒙(HarmonyOS NEXT)应用。技术栈基于 wot-ui 组件库与 vitesse-uni-app,结合VSCode、HBuilderX及DevEco Studio实现开发调试。流程涵盖环境配置、证书申请、真机/模拟器运行及打包发布,运行效果与安卓/iOS一致。需注意:条件编译禁用APP-PLUS(改用APP),plus API大部分不可用。当前构建依赖HBuilderX,未来探索通过app-harmony产物结合DevEco Studio实现CI/CD,为跨平台开发提供新路径。
参考资源
- wot-ui: https://wot-ui.cn/
- DevEco Studio: https://developer.huawei.com/consumer/cn/deveco-studio/
- wot-starter: https://starter.wot-ui.cn/
- uni-app 鸿蒙教程: https://uniapp.dcloud.net.cn/tutorial/harmony/runbuild.html
欢迎评论区沟通、讨论👇👇
收起阅读 »修复vue2 composition-api 中 computed,app环境无法二次更新的问题
vue2项目中使用 @vue/composition-api,h5发现没有任何问题,打包成app就出现了computed无法更新的问题
import { computed, ref } from '@vue/composition-api'
import { onPageScroll } from '@dcloudio/uni-app'
export function usePageScroll() {
const top = ref(0)
onPageScroll(e => {
top.value = e.scrollTop
})
return top
}
const navigationBarHeight = uni.getSystemInfoSync().statusBarHeight || 44
export function useNavbarOpacity(navHeight = navigationBarHeight) {
const scrollTop = usePageScroll()
return computed(() => Math.min(navHeight, scrollTop.value) / navHeight)
}
在app里面就发现导航栏的背景颜色无法随着滚动变透明
后面改成 watch+ref就解决了(不知道什么原因)最终解决方案
vue-composition-plugin.js,在main.ts最开头一行引用,覆盖默认的computed方法
import Vue from 'vue' import * as VueCompositionAPI from '@vue/composition-api' const watch = VueCompositionAPI.watch const ref = VueCompositionAPI.ref // app-plus 有bug 直接使用计算属性,计算属性不会自动更新 // 修复 app 端使用 computed时,第二次修改相关 getter里面的值的时候 // computed 的值不会更新,去掉原先计算属性的懒加载效果 // 强制使用watch加ref实现类似 computed VueCompositionAPI.computed = function(options){ let getter = options let setter if (typeof options === 'object') { getter = options.get setter = options.set } let initVal const refVal = ref(initVal) watch(()=> { initVal = getter() return initVal }, (newVal,oldVal)=>{ setter && setter(newVal,oldVal) refVal.value = newVal }, {deep:true,immediate:true})
return refVal
}
Vue.use(VueCompositionAPI.default)
vue2项目中使用 @vue/composition-api,h5发现没有任何问题,打包成app就出现了computed无法更新的问题
import { computed, ref } from '@vue/composition-api'
import { onPageScroll } from '@dcloudio/uni-app'
export function usePageScroll() {
const top = ref(0)
onPageScroll(e => {
top.value = e.scrollTop
})
return top
}
const navigationBarHeight = uni.getSystemInfoSync().statusBarHeight || 44
export function useNavbarOpacity(navHeight = navigationBarHeight) {
const scrollTop = usePageScroll()
return computed(() => Math.min(navHeight, scrollTop.value) / navHeight)
}
在app里面就发现导航栏的背景颜色无法随着滚动变透明
后面改成 watch+ref就解决了(不知道什么原因)最终解决方案
vue-composition-plugin.js,在main.ts最开头一行引用,覆盖默认的computed方法
import Vue from 'vue' import * as VueCompositionAPI from '@vue/composition-api' const watch = VueCompositionAPI.watch const ref = VueCompositionAPI.ref // app-plus 有bug 直接使用计算属性,计算属性不会自动更新 // 修复 app 端使用 computed时,第二次修改相关 getter里面的值的时候 // computed 的值不会更新,去掉原先计算属性的懒加载效果 // 强制使用watch加ref实现类似 computed VueCompositionAPI.computed = function(options){ let getter = options let setter if (typeof options === 'object') { getter = options.get setter = options.set } let initVal const refVal = ref(initVal) watch(()=> { initVal = getter() return initVal }, (newVal,oldVal)=>{ setter && setter(newVal,oldVal) refVal.value = newVal }, {deep:true,immediate:true})
return refVal
}
Vue.use(VueCompositionAPI.default)
收起阅读 »
从零到一开发鸿蒙6原生时钟应用:uni-app x 完全实战指南
🚀 从零到一开发鸿蒙6原生时钟应用:uni-app x 完全实战指南
前言
鸿蒙6时代已来,纯血鸿蒙应用开发正当时!
在移动应用开发的新纪元,鸿蒙6(HarmonyOS 6)作为华为全新一代操作系统,已经完全摆脱了安卓内核,实现了真正的"纯血鸿蒙"。本文将详细介绍如何使用 uni-app x 框架,从零开发一款功能完整、界面精美的鸿蒙6原生时钟应用,集成时钟、闹钟、秒表、计时器四大核心功能。
为什么选择鸿蒙6?
- 🎯 纯血鸿蒙系统,性能提升30%+
- 🎯 全新的分布式架构,跨设备协同
- 🎯 更强大的安全机制
- 🎯 完善的开发者生态
🌟 项目亮点
- 🎨 Material Design 深色主题:符合鸿蒙6设计规范,夜间使用友好
- 📱 鸿蒙6原生应用:使用 uni-app x 编译为纯血鸿蒙原生应用
- 🚀 完全离线可用:无需网络连接,数据本地存储
- 🎯 代码结构清晰:模块化设计,易于维护和扩展
- ⚡ 极致性能:鸿蒙6平台原生性能,启动速度提升50%
- 🔥 一次开发,多端部署:支持 HarmonyOS 6、Android、iOS、H5、小程序
- 🌐 分布式能力:充分利用鸿蒙6的跨设备协同特性
- 🔐 安全可靠:符合鸿蒙6的安全标准
🛠️ 技术栈
- 核心框架:uni-app x(专为鸿蒙6优化)
- 开发语言:UTS(TypeScript 超集,编译为鸿蒙原生代码)
- 前端框架:Vue 3 Composition API
- 样式方案:SCSS + Material Design
- 数据存储:uni.storage → 鸿蒙6 Preferences API
- 目标平台:HarmonyOS 6(纯血鸿蒙系统)
- API版本:HarmonyOS API 18+
🤔 为什么选择 uni-app x 开发鸿蒙6应用?
uni-app x 是专为鸿蒙6量身打造的跨平台开发框架!
| 对比维度 | 原生开发 | uni-app x | 其他跨平台框架 |
|---|---|---|---|
| 开发语言 | ArkTS | UTS(类TypeScript) | JavaScript/Dart |
| 性能表现 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| 开发效率 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 跨平台能力 | ❌ | ✅ 5端通用 | ✅ 有限支持 |
| 生态支持 | 🟢 官方 | 🟢 官方+社区 | 🟡 社区 |
| 鸿蒙6适配 | ✅ 完美 | ✅ 完美 | 🟡 部分 |
5大核心优势:
-
🚀 真正的原生性能:
- uni-app x 编译为鸿蒙6原生代码(非WebView)
- 启动速度快50%,运行流畅度媲美原生
- 内存占用降低30%
-
💡 开发效率提升200%:
- 使用熟悉的 Vue 3 语法
- 热重载、调试工具完善
- 一套代码,5端运行(HarmonyOS、Android、iOS、H5、小程序)
-
🌐 无缝对接鸿蒙6能力:
- 原生 API 直接调用(通知、振动、后台任务)
- 支持鸿蒙6分布式特性
- 完美适配折叠屏、穿戴设备
-
🎯 生态丰富:
- 10000+ 插件市场
- 活跃的开发者社区
- 完善的文档和示例
-
🔥 DCloud 官方支持:
- 全力支持鸿蒙生态建设
- 及时更新适配鸿蒙最新版本
- 提供技术支持和培训
一、鸿蒙6应用开发环境搭建
1.1 开发环境要求
要使用 uni-app x 开发鸿蒙6应用,需要准备以下环境:
硬件要求:
- CPU:Intel i5 / AMD Ryzen 5 及以上
- 内存:16GB 及以上(推荐32GB)
- 硬盘:至少50GB可用空间(SSD推荐)
软件环境:
✅ 操作系统:
- Windows 10/11(64位)
- macOS 12.0+
✅ 开发工具:
- HBuilderX 4.0+(内置 uni-app x + 鸿蒙6支持)
- 下载地址:https://www.dcloud.io/hbuilderx.html
✅ 鸿蒙开发套件:
- HarmonyOS 6 SDK(API Level 18+)
- DevEco Studio(可选,用于查看API文档)
- 下载地址:https://developer.huawei.com
✅ 开发语言:
- UTS(uni-app TypeScript,编译为鸿蒙原生代码)
✅ 运行时:
- Node.js 18.0+(LTS版本)
- npm 9.0+ 或 pnpm 8.0+
获取开发者账号:
- 注册华为开发者账号:https://developer.huawei.com
- 实名认证(个人或企业)
- 申请应用签名证书
1.2 鸿蒙6应用配置
在 manifest.json 中配置鸿蒙6应用信息(关键配置):
{
"name": "simpleclock",
"appid": "your_app_id",
"description": "多功能时钟应用",
"versionName": "1.0.0",
"versionCode": "100",
"uni-app-x": {
"harmony": {
"minAPIVersion": "12",
"targetAPIVersion": "12",
"compatibleAPIVersion": "12",
"abilities": [
{
"name": "MainAbility",
"description": "时钟应用主界面"
}
],
"permissions": [
"ohos.permission.KEEP_BACKGROUND_RUNNING",
"ohos.permission.NOTIFICATION_CONTROLLER",
"ohos.permission.VIBRATE"
]
}
}
}
1.3 项目架构设计
应用采用经典的 Tab Bar 导航结构,完美适配鸿蒙设计规范:
simpleclock/
├── pages/
│ ├── index/ # 时钟页面(世界时钟)
│ ├── alarm/ # 闹钟页面
│ ├── stopwatch/ # 秒表页面
│ ├── timer/ # 计时器页面
│ └── settings/ # 设置页面
├── App.uvue # 应用入口(.uvue 是 uni-app x 专用格式)
├── pages.json # 页面配置
├── uni.scss # 全局样式变量
└── manifest.json # 应用配置(含鸿蒙配置)
1.4 uni-app x 与传统 uni-app 的核心区别
| 特性 | 传统 uni-app | uni-app x(鸿蒙6) | 优势 |
|---|---|---|---|
| 编译方式 | JavaScript 运行时 | 编译为鸿蒙原生代码 | 性能提升50% |
| 渲染引擎 | WebView | 鸿蒙原生渲染 | 流畅度大幅提升 |
| 性能表现 | 接近原生(80%) | 100%原生性能 | 与原生开发一致 |
| 文件格式 | .vue | .uvue | 支持鸿蒙特性 |
| 开发语言 | JavaScript/TypeScript | UTS | 类型安全,性能更好 |
| 鸿蒙6支持 | 支持纯血鸿蒙 | ✅ 完美支持,未来演进方向 | 无缝对接鸿蒙API |
| 包体积 | 较大(含运行时) | 更小(30%↓) | 启动更快 |
| 热更新 | ✅ 支持 | ⚠️ 受限 | 鸿蒙安全限制 |
重要说明:
- 🔥 鸿蒙6应用必须使用 uni-app x
- 🔥 传统 uni-app 只能运行在 HarmonyOS 兼容模式(非纯血)
- 🔥 新项目强烈推荐直接使用 uni-app x
1.5 配置 pages.json(鸿蒙适配)
首先配置底部导航栏,这里特别注意鸿蒙平台的样式适配:
{
"tabBar": {
"color": "#999999",
"selectedColor": "#FF6B00",
"backgroundColor": "#2C2C2C",
"borderStyle": "black",
"list": [
{
"pagePath": "pages/index/index",
"text": "时钟"
},
{
"pagePath": "pages/alarm/alarm",
"text": "闹钟"
},
{
"pagePath": "pages/stopwatch/stopwatch",
"text": "秒表"
},
{
"pagePath": "pages/timer/timer",
"text": "计时器"
}
]
}
}
1.6 鸿蒙平台条件编译
uni-app x 提供了强大的条件编译能力,可以针对鸿蒙平台做特殊处理:
// #ifdef APP-HARMONY
// 鸿蒙平台特有代码
import { vibrator } from '@kit.SensorServiceKit'
import { notificationManager } from '@kit.NotificationKit'
// #endif
// #ifdef APP-ANDROID
// Android 平台代码
// #endif
// #ifndef APP-HARMONY
// 非鸿蒙平台代码
// #endif
1.7 Material Design 深色主题配置(鸿蒙适配)
在 uni.scss 中定义全局颜色变量,完美适配鸿蒙深色模式:
/* Material Design 深色主题颜色 */
$primary-color: #FF6B00; // 主色调(橙色)
$background-dark: #212121; // 深色背景
$background-card: #2C2C2C; // 卡片背景
$background-elevated: #3A3A3A; // 高亮背景
$text-primary: #FFFFFF; // 主要文本
$text-secondary: #CCCCCC; // 次要文本
$text-disabled: #999999; // 禁用文本
二、核心功能实现(鸿蒙原生 API 集成)
2.1 时钟页面 - 实时时间与多时区显示
2.1.1 实时时间更新(鸿蒙优化版)
在鸿蒙平台上,我们可以使用更高效的定时器机制。uni-app x 的 setInterval 会被编译为鸿蒙原生定时器:
export default {
data() {
return {
currentTime: '9:23',
period: 'a.m.',
currentDate: 'Mon, 18 January',
timeFormat: 12,
timer: null
}
},
onLoad() {
this.updateTime()
this.startTimer()
},
methods: {
updateTime() {
const now = new Date()
const hours = now.getHours()
const minutes = now.getMinutes()
if (this.timeFormat === 12) {
const displayHours = hours % 12 || 12
this.currentTime = displayHours + ':' +
(minutes < 10 ? '0' : '') + minutes
this.period = hours >= 12 ? 'p.m.' : 'a.m.'
} else {
this.currentTime = (hours < 10 ? '0' : '') + hours + ':' +
(minutes < 10 ? '0' : '') + minutes
this.period = ''
}
},
startTimer() {
this.timer = setInterval(() => {
this.updateTime()
}, 1000)
}
}
}
2.1.2 时间格式切换与鸿蒙数据持久化
使用 uni.storage API,在鸿蒙平台上会自动映射到鸿蒙的 Preferences 数据存储:
setTimeFormat(format) {
this.timeFormat = format
this.updateTime()
// 在鸿蒙平台会自动使用 Preferences API
uni.setStorageSync('timeFormat', format)
}
// 鸿蒙平台的存储路径
// /data/storage/el2/base/preferences/时钟应用/
2.1.3 鸿蒙系统时间获取优化
// #ifdef APP-HARMONY
import { systemDateTime } from '@kit.BasicServicesKit'
// 获取鸿蒙系统时间(更精确)
const harmonyTime = systemDateTime.getCurrentTime()
// #endif
2.2 闹钟页面 - 鸿蒙通知与后台任务
2.2.1 鸿蒙权限申请
在开发闹钟功能前,需要申请鸿蒙相关权限:
// manifest.json - 鸿蒙权限配置
{
"uni-app-x": {
"harmony": {
"permissions": [
"ohos.permission.NOTIFICATION_CONTROLLER", // 通知权限
"ohos.permission.VIBRATE", // 振动权限
"ohos.permission.KEEP_BACKGROUND_RUNNING" // 后台运行
],
"backgroundModes": ["dataTransfer", "audioPlayback"]
}
}
}
// 运行时请求权限(鸿蒙平台)
// #ifdef APP-HARMONY
import { abilityAccessCtrl } from '@kit.AbilityKit'
async function requestPermissions() {
const atManager = abilityAccessCtrl.createAtManager()
try {
await atManager.requestPermissionsFromUser(getContext(), [
'ohos.permission.NOTIFICATION_CONTROLLER',
'ohos.permission.VIBRATE'
])
} catch (err) {
console.error('权限申请失败', err)
}
}
// #endif
2.2.2 数据结构设计
闹钟数据结构设计如下:
{
time: '08:00', // 时间
label: '起床', // 标签
repeat: [false, true, ...], // 重复(周日到周六)
ringtone: 0, // 铃声索引
vibrate: true, // 振动
gradualVolume: true, // 音量渐增
snooze: 10, // 贪睡时长(分钟)
enabled: true // 是否启用
}
2.2.3 鸿蒙本地存储实现
uni.storage 在鸿蒙平台会自动使用 Preferences API,提供高性能的键值对存储:
methods: {
// 加载闹钟
loadAlarms() {
const saved = uni.getStorageSync('alarms')
if (saved) {
this.alarms = JSON.parse(saved)
}
},
// 保存闹钟
saveAlarms() {
uni.setStorageSync('alarms', JSON.stringify(this.alarms))
},
// 添加/编辑闹钟
saveAlarm() {
if (this.editIndex >= 0) {
// 编辑现有闹钟
this.alarms[this.editIndex] = this.editAlarmData
} else {
// 添加新闹钟
this.alarms.push(this.editAlarmData)
}
this.saveAlarms()
this.closeDialog()
},
// 删除闹钟
deleteAlarm() {
uni.showModal({
title: '确认删除',
content: '确定要删除这个闹钟吗?',
success: (res) => {
if (res.confirm) {
this.alarms.splice(this.editIndex, 1)
this.saveAlarms()
}
}
})
}
}
2.2.4 鸿蒙通知发送
使用鸿蒙原生通知 API 发送闹钟提醒:
// #ifdef APP-HARMONY
import notificationManager from '@ohos.notificationManager'
// 发送鸿蒙通知
async function sendAlarmNotification(alarmData) {
const notificationRequest = {
id: alarmData.id,
content: {
contentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
normal: {
title: '闹钟提醒',
text: alarmData.label || '该起床了!',
additionalText: new Date().toLocaleTimeString()
}
},
actionButtons: [
{
title: '贪睡',
wantAgent: createSnoozeWantAgent()
},
{
title: '关闭',
wantAgent: createCloseWantAgent()
}
]
}
try {
await notificationManager.publish(notificationRequest)
} catch (err) {
console.error('通知发送失败', err)
}
}
// #endif
// 跨平台兼容写法
function sendNotification(alarmData) {
// #ifdef APP-HARMONY
sendAlarmNotification(alarmData)
// #endif
// #ifndef APP-HARMONY
uni.showModal({
title: '闹钟提醒',
content: alarmData.label || '该起床了!'
})
// #endif
}
2.2.5 鸿蒙振动反馈
// #ifdef APP-HARMONY
import vibrator from '@ohos.vibrator'
// 鸿蒙振动效果
function vibrateHarmony() {
try {
// 预定义振动效果
vibrator.startVibration({
type: 'preset',
effectId: 'haptic.clock.timer',
count: 3
})
// 或自定义振动
vibrator.startVibration({
type: 'time',
duration: 1000
})
} catch (err) {
console.error('振动失败', err)
}
}
// #endif
// 跨平台振动封装
function triggerVibration() {
// #ifdef APP-HARMONY
vibrateHarmony()
// #endif
// #ifndef APP-HARMONY
uni.vibrateShort()
// #endif
}
2.2.6 弹窗表单设计
使用自定义弹窗组件实现闹钟编辑界面:
<view v-if="showDialog" class="dialog-mask" @click="closeDialog">
<view class="dialog" @click.stop>
<view class="dialog-header">
<text class="dialog-title">
{{editIndex >= 0 ? '编辑闹钟' : '添加闹钟'}}
</text>
</view>
<view class="dialog-content">
<!-- 时间选择器 -->
<picker mode="time" :value="editAlarmData.time"
@change="onTimeChange">
<text>{{editAlarmData.time}}</text>
</picker>
<!-- 周重复选择 -->
<view class="week-selector">
<text v-for="(day, i) in weekDays" :key="i"
:class="{'active': editAlarmData.repeat[i]}"
@click="toggleDay(i)">
{{day}}
</text>
</view>
</view>
</view>
</view>
2.3 秒表页面 - 高精度计时与圈数记录
2.3.1 高精度计时实现
使用 10ms 间隔实现百分之一秒精度:
export default {
data() {
return {
isRunning: false,
totalTime: 0, // 总时间(毫秒)
startTime: 0,
timer: null,
laps: [] // 圈数记录
}
},
computed: {
formattedTime() {
const totalSeconds = Math.floor(this.totalTime / 1000)
const minutes = Math.floor(totalSeconds / 60)
const seconds = totalSeconds % 60
return (minutes < 10 ? '0' : '') + minutes + ':' +
(seconds < 10 ? '0' : '') + seconds
},
milliseconds() {
return String(Math.floor((this.totalTime % 1000) / 10))
.padStart(2, '0')
}
},
methods: {
start() {
this.isRunning = true
this.startTime = Date.now() - this.totalTime
this.timer = setInterval(() => {
this.totalTime = Date.now() - this.startTime
}, 10) // 10ms 更新一次
}
}
}
2.3.2 圈数记录与排序
实现圈数记录和多种排序方式:
methods: {
recordLap() {
const lapTime = this.totalTime - this.lastLapTime
this.lapCounter++
this.laps.unshift({
id: Date.now(),
index: this.lapCounter,
time: this.formatTime(lapTime),
rawTime: lapTime
})
this.lastLapTime = this.totalTime
}
},
computed: {
sortedLaps() {
let sorted = [...this.laps]
// 根据排序模式排序
if (this.sortMode === 'fastest') {
sorted.sort((a, b) => a.rawTime - b.rawTime)
} else if (this.sortMode === 'slowest') {
sorted.sort((a, b) => b.rawTime - a.rawTime)
}
// 标记最快和最慢
if (sorted.length > 1) {
const times = this.laps.map(l => l.rawTime)
const fastest = Math.min(...times)
const slowest = Math.max(...times)
sorted = sorted.map(lap => ({
...lap,
isFastest: lap.rawTime === fastest,
isSlowest: lap.rawTime === slowest && fastest !== slowest
}))
}
return sorted
}
}
2.4 计时器页面 - 倒计时与进度显示
2.4.1 时间选择器实现
使用 picker-view 实现时间选择:
<view class="picker-row">
<view class="picker-group">
<picker-view class="picker" :value="[hours]"
@change="onHoursChange">
<picker-view-column>
<view v-for="n in 24" :key="n" class="picker-item">
<text>{{n - 1}}</text>
</view>
</picker-view-column>
</picker-view>
<text class="picker-label">小时</text>
</view>
<!-- 分钟和秒的选择器类似 -->
</view>
2.4.2 进度条实现
实时计算并显示倒计时进度:
computed: {
progressPercent() {
if (this.totalSeconds === 0) return 0
return Math.floor(
(this.totalSeconds - this.remainingTime) /
this.totalSeconds * 100
)
}
}
<view class="progress-bar">
<view class="progress-fill"
:style="{width: progressPercent + '%'}">
</view>
</view>
2.4.3 倒计时完成处理
onTimerComplete() {
this.stopTimer()
this.isRunning = false
// 振动提醒
if (this.vibrate) {
// uni.vibrateLong()
}
// 弹窗通知
if (this.notification) {
uni.showModal({
title: '计时完成',
content: '计时器时间已到!',
showCancel: false
})
}
}
2.5 设置页面 - 丰富的个性化选项
2.5.1 屏幕常亮功能
onKeepScreenOnChange(e) {
this.keepScreenOn = e.detail.value
uni.setStorageSync('keepScreenOn', this.keepScreenOn)
uni.setKeepScreenOn({
keepScreenOn: this.keepScreenOn
})
uni.showToast({
title: this.keepScreenOn ?
'屏幕常亮已开启' : '屏幕常亮已关闭',
icon: 'none'
})
}
2.5.2 数据清除功能
clearData() {
uni.showModal({
title: '确认清除',
content: '此操作将清除所有闹钟和设置数据,无法恢复。确定要继续吗?',
confirmText: '清除',
confirmColor: '#F44336',
success: (res) => {
if (res.confirm) {
uni.clearStorageSync()
uni.showToast({
title: '数据已清除',
icon: 'success'
})
// 重启应用
setTimeout(() => {
uni.reLaunch({
url: '/pages/index/index'
})
}, 1500)
}
}
})
}
三、UI/UX 设计与优化
3.1 Material Design 样式实现
3.1.1 卡片阴影效果
.card {
background-color: #2C2C2C;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}
3.1.2 悬浮按钮(FAB)
.add-btn {
position: fixed;
right: 20px;
bottom: 100px;
width: 60px;
height: 60px;
background-color: #FF6B00;
border-radius: 30px;
display: flex;
justify-content: center;
align-items: center;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}
3.2 颜色系统
深色主题配色方案:
| 元素 | 颜色值 | 用途 |
|---|---|---|
| 主色 | #FF6B00 | 按钮、选中状态、导航栏 |
| 背景 | #212121 | 页面背景 |
| 卡片 | #2C2C2C | 列表项、卡片背景 |
| 分隔 | #3A3A3A | 边框、分隔线 |
| 文本主 | #FFFFFF | 主要文本 |
| 文本次 | #CCCCCC | 次要文本 |
| 文本禁 | #999999 | 禁用文本 |
3.3 交互优化
3.3.1 空状态设计
<view v-if="alarms.length === 0" class="empty-state">
<text class="empty-text">暂无闹钟</text>
<text class="empty-hint">点击下方 "+" 按钮添加闹钟</text>
</view>
3.3.2 加载状态与反馈
// 操作成功提示
uni.showToast({
title: '保存成功',
icon: 'success'
})
// 确认对话框
uni.showModal({
title: '确认删除',
content: '确定要删除这个闹钟吗?',
success: (res) => {
if (res.confirm) {
// 执行删除
}
}
})
四、性能优化
4.1 计时器管理
确保在页面卸载时清除定时器,避免内存泄漏:
export default {
onUnload() {
this.stopTimer()
},
methods: {
stopTimer() {
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
}
}
}
4.2 数据缓存策略
- 使用
uni.setStorageSync同步存储关键数据 - 页面加载时从本地读取数据,减少计算
- 数据变更时立即保存,确保数据一致性
4.3 列表渲染优化
使用 :key 提升列表渲染性能:
<view v-for="(alarm, index) in alarms"
:key="alarm.id || index"
class="alarm-item">
<!-- 内容 -->
</view>
五、鸿蒙平台深度适配
5.1 鸿蒙返回键处理
鸿蒙系统的返回键需要特殊处理,实现双击退出:
// #ifdef APP-HARMONY
let firstBackTime = 0
export default {
onLastPageBackPress: function () {
console.log('HarmonyOS 返回键按下')
if (firstBackTime == 0) {
uni.showToast({
title: '再按一次退出应用',
position: 'bottom',
})
firstBackTime = Date.now()
setTimeout(() => {
firstBackTime = 0
}, 2000)
} else if (Date.now() - firstBackTime < 2000) {
uni.exit() // 退出应用
}
}
}
// #endif
5.2 鸿蒙屏幕常亮
利用鸿蒙的电源管理 API 实现屏幕常亮:
// #ifdef APP-HARMONY
import { runningLock } from '@kit.BasicServicesKit'
let screenLock = null
// 保持屏幕常亮
function keepScreenOn() {
try {
screenLock = runningLock.createRunningLock('screen',
runningLock.RunningLockType.RUNNINGLOCK_SCREEN)
screenLock.lock(0) // 0 表示永久锁定
} catch (err) {
console.error('屏幕常亮失败', err)
}
}
// 释放屏幕锁
function releaseScreenLock() {
if (screenLock) {
screenLock.unlock()
screenLock = null
}
}
// #endif
// 跨平台封装
function setKeepScreenOn(keep) {
// #ifdef APP-HARMONY
if (keep) {
keepScreenOn()
} else {
releaseScreenLock()
}
// #endif
// #ifndef APP-HARMONY
uni.setKeepScreenOn({ keepScreenOn: keep })
// #endif
}
5.3 鸿蒙后台任务
实现闹钟的后台运行:
// #ifdef APP-HARMONY
import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager'
// 申请长时任务
async function requestBackgroundTask() {
try {
await backgroundTaskManager.requestSuspendDelay('时钟后台任务', () => {
console.log('后台任务即将被挂起')
// 保存状态
})
} catch (err) {
console.error('后台任务申请失败', err)
}
}
// #endif
5.4 鸿蒙安全区域适配
考虑鸿蒙设备的刘海屏、水滴屏和折叠屏:
.container {
// 鸿蒙安全区域
padding-top: env(safe-area-inset-top);
padding-bottom: env(safe-area-inset-bottom);
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
}
// 鸿蒙折叠屏适配
@media screen and (min-width: 600px) {
.container {
max-width: 600px;
margin: 0 auto;
}
}
5.5 鸿蒙生命周期适配
export default {
// 应用前台
onShow() {
// #ifdef APP-HARMONY
console.log('HarmonyOS 应用进入前台')
this.resumeTimers() // 恢复计时器
// #endif
},
// 应用后台
onHide() {
// #ifdef APP-HARMONY
console.log('HarmonyOS 应用进入后台')
this.pauseTimers() // 暂停计时器
this.saveState() // 保存状态
// #endif
}
}
5.6 鸿蒙深色模式适配
鸿蒙系统的深色模式可以自动切换:
// #ifdef APP-HARMONY
import { configuration } from '@kit.AbilityKit'
// 监听系统主题变化
function watchThemeChange() {
const config = configuration.getConfiguration()
const isDark = config.colorMode === configuration.ColorMode.COLOR_MODE_DARK
// 应用深色主题
if (isDark) {
this.applyDarkTheme()
} else {
this.applyLightTheme()
}
}
// #endif
六、鸿蒙应用测试与调试
6.1 鸿蒙开发者模式
在鸿蒙设备上启用开发者模式:
- 进入"设置" → "关于手机"
- 连续点击"版本号"7次
- 返回"设置" → "系统和更新" → "开发者选项"
- 开启"USB 调试"和"USB 安装"
6.2 HBuilderX 真机调试
# 1. 连接鸿蒙设备
# 2. 在 HBuilderX 中选择运行设备
# 3. 点击"运行" → "运行到手机或模拟器" → "运行到 HarmonyOS"
# 查看日志
hdc shell hilog | grep "simpleclock"
6.3 鸿蒙特有功能测试清单
- [ ] 通知权限:闹钟通知是否正常显示
- [ ] 振动权限:振动反馈是否生效
- [ ] 后台任务:应用后台时闹钟是否正常工作
- [ ] 屏幕常亮:前台运行时屏幕是否保持常亮
- [ ] 数据持久化:应用重启后数据是否保留
- [ ] 深色模式:系统切换主题时应用是否自动适配
- [ ] 折叠屏适配:在折叠屏设备上布局是否正常
- [ ] 返回键处理:双击返回是否正常退出
6.4 性能测试(鸿蒙平台)
// 使用鸿蒙性能追踪
// #ifdef APP-HARMONY
import hiTraceMeter from '@ohos.hiTraceMeter'
// 开始性能追踪
hiTraceMeter.startTrace('stopwatch_timing', 1001)
// 执行计时逻辑
this.updateStopwatchTime()
// 结束追踪
hiTraceMeter.finishTrace('stopwatch_timing', 1001)
// #endif
6.2 边界情况处理
// 防止重复点击
if (this.totalSeconds === 0) {
uni.showToast({
title: '请设置计时时间',
icon: 'none'
})
return
}
// 数据验证
if (!editAlarmData.time) {
uni.showToast({
title: '请选择时间',
icon: 'none'
})
return
}
七、项目总结
7.1 技术亮点
- 完整的功能闭环:从时间显示到提醒管理,覆盖用户全部需求
- 优雅的代码结构:模块化设计,职责清晰
- 良好的用户体验:Material Design + 深色主题
- 高性能实现:合理使用定时器,优化渲染性能
- 跨平台兼容:一套代码,多端运行
7.2 可扩展方向
- 云同步功能:支持多设备数据同步
- 更多铃声:支持自定义铃声上传
- 统计分析:记录使用习惯,提供数据分析
- 智能提醒:根据用户习惯智能推荐闹钟时间
- 主题定制:支持更多颜色主题
7.3 开发心得
- 用户体验至上:每一个交互细节都要仔细打磨
- 代码质量:保持代码简洁,注重可维护性
- 性能优化:及时清理资源,避免内存泄漏
- 测试驱动:充分测试各种边界情况
- 持续迭代:根据用户反馈不断优化改进
八、源码与资源
8.1 项目结构
simpleclock/
├── pages/
│ ├── index/index.uvue # 264 行
│ ├── alarm/alarm.uvue # 430 行
│ ├── stopwatch/stopwatch.uvue # 280 行
│ ├── timer/timer.uvue # 300 行
│ └── settings/settings.uvue # 350 行
├── App.uvue # 124 行
├── pages.json # 76 行
├── uni.scss # 77 行
├── README.md # 项目说明
└── 使用指南.md # 用户手册
8.2 代码统计
- 总代码量:约 1900+ 行
- 页面数:5 个主要页面
- 组件数:多个自定义组件
- 功能模块:4 个核心功能
8.3 运行环境
- 开发工具:HBuilderX 4.0+
- uni-app x 版本:最新版
- 鸿蒙系统:HarmonyOS 6
- 鸿蒙 SDK:API Level 10+
- 测试设备:
- 华为 Mate 60 系列(HarmonyOS 6)
- 华为 MatePad Pro(折叠屏测试)
- 鸿蒙模拟器
8.4 鸿蒙应用发布
8.4.1 应用签名
# 在华为开发者联盟申请应用签名
# 下载签名文件并配置到 manifest.json
{
"uni-app-x": {
"harmony": {
"signing": {
"profile": "path/to/profile.p7b",
"certFile": "path/to/cert.pem",
"keyFile": "path/to/key.p12",
"keyPassword": "your_password"
}
}
}
}
8.4.2 打包发布
# HBuilderX 中操作:
# 1. 发行 → 原生App-云打包
# 2. 选择 HarmonyOS 平台
# 3. 填写应用信息和签名配置
# 4. 点击打包,等待云端编译
# 5. 下载 .hap 安装包
# 命令行打包(可选)
npm run build:app-harmony
8.4.3 应用上架
- 登录华为开发者联盟
- 进入"应用服务" → "AppGallery Connect"
- 创建应用并上传 .hap 包
- 填写应用信息(截图、描述、分类)
- 提交审核
- 审核通过后上架
九、鸿蒙开发常见问题
9.1 权限被拒绝
问题:应用无法发送通知或振动
解决:
// 在应用启动时主动请求权限
// #ifdef APP-HARMONY
async function checkAndRequestPermissions() {
const permissions = [
'ohos.permission.NOTIFICATION_CONTROLLER',
'ohos.permission.VIBRATE'
]
for (let permission of permissions) {
const result = await checkPermission(permission)
if (!result) {
await requestPermission(permission)
}
}
}
// #endif
9.2 后台任务被终止
问题:闹钟在后台不响
解决:申请长时任务授权,确保应用在后台持续运行
9.3 数据丢失
问题:应用卸载后数据丢失
解决:使用鸿蒙云空间同步数据(需要用户登录华为账号)
9.4 性能问题
问题:页面切换卡顿
解决:
- 使用虚拟列表优化长列表渲染
- 避免在主线程执行耗时操作
- 使用鸿蒙的 Worker 线程处理复杂计算
结语
本文详细介绍了如何使用 uni-app x 开发一款功能完整的鸿蒙6原生时钟应用,从环境搭建、核心功能实现、鸿蒙6平台深度适配到应用发布,涵盖了鸿蒙6应用开发的各个方面。
🎉 这是一个真实的鸿蒙6原生应用开发案例!
核心要点回顾
-
uni-app x 的优势:
- ✅ 编译为鸿蒙原生代码,性能卓越
- ✅ 一套代码多端运行,大幅降低开发成本
- ✅ 丰富的 API 支持,无缝对接鸿蒙能力
-
鸿蒙平台特性:
- ✅ 强大的通知系统
- ✅ 完善的权限管理
- ✅ 优秀的后台任务机制
- ✅ 深度的系统集成
-
开发建议:
- 💡 充分利用条件编译,针对鸿蒙做优化
- 💡 注意权限申请和用户体验
- 💡 重视性能优化和内存管理
- 💡 遵循鸿蒙设计规范
希望这篇文章能帮助你:
- 🎯 零基础入门:从环境搭建到应用发布的完整流程
- 🎨 深度理解鸿蒙6:掌握鸿蒙6的核心特性和设计理念
- 💡 实战经验:学习时间管理类应用的最佳实践
- 🚀 快速上手:30分钟开发出第一个鸿蒙6应用
- 🔥 抓住机遇:在鸿蒙生态爆发期占据先机
下一步计划
- [ ] 适配鸿蒙折叠屏设备
- [ ] 接入华为账号系统
- [ ] 实现华为云空间数据同步
- [ ] 添加鸿蒙小部件(Widget)
- [ ] 接入鸿蒙分布式能力
如果你有任何问题或建议,欢迎交流讨论!让我们一起为鸿蒙生态贡献力量!🚀
关键词:#鸿蒙6 #HarmonyOS6 #uni-app-x #纯血鸿蒙 #原生应用开发 #Vue3 #跨平台开发 #时钟应用
作者:坚果
日期:2025年10月28日
项目地址:GitCode - simpleclock
开源协议:MIT License
📢 开源说明
本项目完全开源,欢迎:
- ⭐ Star 项目,关注后续更新
- 🍴 Fork 项目,二次开发
- 🐛 提交 Issue,反馈问题
- 🤝 提交 PR,贡献代码
当前状态:
- ✅ 核心功能已完成
- ⚠️ 部分细节待优化(如时区同步、铃声播放等)
- 🎯 持续迭代中,欢迎贡献代码!
🔗 相关资源
💬 交流讨论
如果你在开发过程中遇到问题,或有好的想法和建议:
- 💬 欢迎在项目 Issue 区讨论
让我们一起推动鸿蒙生态发展! 🚀🇨🇳
🚀 从零到一开发鸿蒙6原生时钟应用:uni-app x 完全实战指南
前言
鸿蒙6时代已来,纯血鸿蒙应用开发正当时!
在移动应用开发的新纪元,鸿蒙6(HarmonyOS 6)作为华为全新一代操作系统,已经完全摆脱了安卓内核,实现了真正的"纯血鸿蒙"。本文将详细介绍如何使用 uni-app x 框架,从零开发一款功能完整、界面精美的鸿蒙6原生时钟应用,集成时钟、闹钟、秒表、计时器四大核心功能。
为什么选择鸿蒙6?
- 🎯 纯血鸿蒙系统,性能提升30%+
- 🎯 全新的分布式架构,跨设备协同
- 🎯 更强大的安全机制
- 🎯 完善的开发者生态
🌟 项目亮点
- 🎨 Material Design 深色主题:符合鸿蒙6设计规范,夜间使用友好
- 📱 鸿蒙6原生应用:使用 uni-app x 编译为纯血鸿蒙原生应用
- 🚀 完全离线可用:无需网络连接,数据本地存储
- 🎯 代码结构清晰:模块化设计,易于维护和扩展
- ⚡ 极致性能:鸿蒙6平台原生性能,启动速度提升50%
- 🔥 一次开发,多端部署:支持 HarmonyOS 6、Android、iOS、H5、小程序
- 🌐 分布式能力:充分利用鸿蒙6的跨设备协同特性
- 🔐 安全可靠:符合鸿蒙6的安全标准
🛠️ 技术栈
- 核心框架:uni-app x(专为鸿蒙6优化)
- 开发语言:UTS(TypeScript 超集,编译为鸿蒙原生代码)
- 前端框架:Vue 3 Composition API
- 样式方案:SCSS + Material Design
- 数据存储:uni.storage → 鸿蒙6 Preferences API
- 目标平台:HarmonyOS 6(纯血鸿蒙系统)
- API版本:HarmonyOS API 18+
🤔 为什么选择 uni-app x 开发鸿蒙6应用?
uni-app x 是专为鸿蒙6量身打造的跨平台开发框架!
| 对比维度 | 原生开发 | uni-app x | 其他跨平台框架 |
|---|---|---|---|
| 开发语言 | ArkTS | UTS(类TypeScript) | JavaScript/Dart |
| 性能表现 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| 开发效率 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 跨平台能力 | ❌ | ✅ 5端通用 | ✅ 有限支持 |
| 生态支持 | 🟢 官方 | 🟢 官方+社区 | 🟡 社区 |
| 鸿蒙6适配 | ✅ 完美 | ✅ 完美 | 🟡 部分 |
5大核心优势:
-
🚀 真正的原生性能:
- uni-app x 编译为鸿蒙6原生代码(非WebView)
- 启动速度快50%,运行流畅度媲美原生
- 内存占用降低30%
-
💡 开发效率提升200%:
- 使用熟悉的 Vue 3 语法
- 热重载、调试工具完善
- 一套代码,5端运行(HarmonyOS、Android、iOS、H5、小程序)
-
🌐 无缝对接鸿蒙6能力:
- 原生 API 直接调用(通知、振动、后台任务)
- 支持鸿蒙6分布式特性
- 完美适配折叠屏、穿戴设备
-
🎯 生态丰富:
- 10000+ 插件市场
- 活跃的开发者社区
- 完善的文档和示例
-
🔥 DCloud 官方支持:
- 全力支持鸿蒙生态建设
- 及时更新适配鸿蒙最新版本
- 提供技术支持和培训
一、鸿蒙6应用开发环境搭建
1.1 开发环境要求
要使用 uni-app x 开发鸿蒙6应用,需要准备以下环境:
硬件要求:
- CPU:Intel i5 / AMD Ryzen 5 及以上
- 内存:16GB 及以上(推荐32GB)
- 硬盘:至少50GB可用空间(SSD推荐)
软件环境:
✅ 操作系统:
- Windows 10/11(64位)
- macOS 12.0+
✅ 开发工具:
- HBuilderX 4.0+(内置 uni-app x + 鸿蒙6支持)
- 下载地址:https://www.dcloud.io/hbuilderx.html
✅ 鸿蒙开发套件:
- HarmonyOS 6 SDK(API Level 18+)
- DevEco Studio(可选,用于查看API文档)
- 下载地址:https://developer.huawei.com
✅ 开发语言:
- UTS(uni-app TypeScript,编译为鸿蒙原生代码)
✅ 运行时:
- Node.js 18.0+(LTS版本)
- npm 9.0+ 或 pnpm 8.0+
获取开发者账号:
- 注册华为开发者账号:https://developer.huawei.com
- 实名认证(个人或企业)
- 申请应用签名证书
1.2 鸿蒙6应用配置
在 manifest.json 中配置鸿蒙6应用信息(关键配置):
{
"name": "simpleclock",
"appid": "your_app_id",
"description": "多功能时钟应用",
"versionName": "1.0.0",
"versionCode": "100",
"uni-app-x": {
"harmony": {
"minAPIVersion": "12",
"targetAPIVersion": "12",
"compatibleAPIVersion": "12",
"abilities": [
{
"name": "MainAbility",
"description": "时钟应用主界面"
}
],
"permissions": [
"ohos.permission.KEEP_BACKGROUND_RUNNING",
"ohos.permission.NOTIFICATION_CONTROLLER",
"ohos.permission.VIBRATE"
]
}
}
}
1.3 项目架构设计
应用采用经典的 Tab Bar 导航结构,完美适配鸿蒙设计规范:
simpleclock/
├── pages/
│ ├── index/ # 时钟页面(世界时钟)
│ ├── alarm/ # 闹钟页面
│ ├── stopwatch/ # 秒表页面
│ ├── timer/ # 计时器页面
│ └── settings/ # 设置页面
├── App.uvue # 应用入口(.uvue 是 uni-app x 专用格式)
├── pages.json # 页面配置
├── uni.scss # 全局样式变量
└── manifest.json # 应用配置(含鸿蒙配置)
1.4 uni-app x 与传统 uni-app 的核心区别
| 特性 | 传统 uni-app | uni-app x(鸿蒙6) | 优势 |
|---|---|---|---|
| 编译方式 | JavaScript 运行时 | 编译为鸿蒙原生代码 | 性能提升50% |
| 渲染引擎 | WebView | 鸿蒙原生渲染 | 流畅度大幅提升 |
| 性能表现 | 接近原生(80%) | 100%原生性能 | 与原生开发一致 |
| 文件格式 | .vue | .uvue | 支持鸿蒙特性 |
| 开发语言 | JavaScript/TypeScript | UTS | 类型安全,性能更好 |
| 鸿蒙6支持 | 支持纯血鸿蒙 | ✅ 完美支持,未来演进方向 | 无缝对接鸿蒙API |
| 包体积 | 较大(含运行时) | 更小(30%↓) | 启动更快 |
| 热更新 | ✅ 支持 | ⚠️ 受限 | 鸿蒙安全限制 |
重要说明:
- 🔥 鸿蒙6应用必须使用 uni-app x
- 🔥 传统 uni-app 只能运行在 HarmonyOS 兼容模式(非纯血)
- 🔥 新项目强烈推荐直接使用 uni-app x
1.5 配置 pages.json(鸿蒙适配)
首先配置底部导航栏,这里特别注意鸿蒙平台的样式适配:
{
"tabBar": {
"color": "#999999",
"selectedColor": "#FF6B00",
"backgroundColor": "#2C2C2C",
"borderStyle": "black",
"list": [
{
"pagePath": "pages/index/index",
"text": "时钟"
},
{
"pagePath": "pages/alarm/alarm",
"text": "闹钟"
},
{
"pagePath": "pages/stopwatch/stopwatch",
"text": "秒表"
},
{
"pagePath": "pages/timer/timer",
"text": "计时器"
}
]
}
}
1.6 鸿蒙平台条件编译
uni-app x 提供了强大的条件编译能力,可以针对鸿蒙平台做特殊处理:
// #ifdef APP-HARMONY
// 鸿蒙平台特有代码
import { vibrator } from '@kit.SensorServiceKit'
import { notificationManager } from '@kit.NotificationKit'
// #endif
// #ifdef APP-ANDROID
// Android 平台代码
// #endif
// #ifndef APP-HARMONY
// 非鸿蒙平台代码
// #endif
1.7 Material Design 深色主题配置(鸿蒙适配)
在 uni.scss 中定义全局颜色变量,完美适配鸿蒙深色模式:
/* Material Design 深色主题颜色 */
$primary-color: #FF6B00; // 主色调(橙色)
$background-dark: #212121; // 深色背景
$background-card: #2C2C2C; // 卡片背景
$background-elevated: #3A3A3A; // 高亮背景
$text-primary: #FFFFFF; // 主要文本
$text-secondary: #CCCCCC; // 次要文本
$text-disabled: #999999; // 禁用文本
二、核心功能实现(鸿蒙原生 API 集成)
2.1 时钟页面 - 实时时间与多时区显示
2.1.1 实时时间更新(鸿蒙优化版)
在鸿蒙平台上,我们可以使用更高效的定时器机制。uni-app x 的 setInterval 会被编译为鸿蒙原生定时器:
export default {
data() {
return {
currentTime: '9:23',
period: 'a.m.',
currentDate: 'Mon, 18 January',
timeFormat: 12,
timer: null
}
},
onLoad() {
this.updateTime()
this.startTimer()
},
methods: {
updateTime() {
const now = new Date()
const hours = now.getHours()
const minutes = now.getMinutes()
if (this.timeFormat === 12) {
const displayHours = hours % 12 || 12
this.currentTime = displayHours + ':' +
(minutes < 10 ? '0' : '') + minutes
this.period = hours >= 12 ? 'p.m.' : 'a.m.'
} else {
this.currentTime = (hours < 10 ? '0' : '') + hours + ':' +
(minutes < 10 ? '0' : '') + minutes
this.period = ''
}
},
startTimer() {
this.timer = setInterval(() => {
this.updateTime()
}, 1000)
}
}
}
2.1.2 时间格式切换与鸿蒙数据持久化
使用 uni.storage API,在鸿蒙平台上会自动映射到鸿蒙的 Preferences 数据存储:
setTimeFormat(format) {
this.timeFormat = format
this.updateTime()
// 在鸿蒙平台会自动使用 Preferences API
uni.setStorageSync('timeFormat', format)
}
// 鸿蒙平台的存储路径
// /data/storage/el2/base/preferences/时钟应用/
2.1.3 鸿蒙系统时间获取优化
// #ifdef APP-HARMONY
import { systemDateTime } from '@kit.BasicServicesKit'
// 获取鸿蒙系统时间(更精确)
const harmonyTime = systemDateTime.getCurrentTime()
// #endif
2.2 闹钟页面 - 鸿蒙通知与后台任务
2.2.1 鸿蒙权限申请
在开发闹钟功能前,需要申请鸿蒙相关权限:
// manifest.json - 鸿蒙权限配置
{
"uni-app-x": {
"harmony": {
"permissions": [
"ohos.permission.NOTIFICATION_CONTROLLER", // 通知权限
"ohos.permission.VIBRATE", // 振动权限
"ohos.permission.KEEP_BACKGROUND_RUNNING" // 后台运行
],
"backgroundModes": ["dataTransfer", "audioPlayback"]
}
}
}
// 运行时请求权限(鸿蒙平台)
// #ifdef APP-HARMONY
import { abilityAccessCtrl } from '@kit.AbilityKit'
async function requestPermissions() {
const atManager = abilityAccessCtrl.createAtManager()
try {
await atManager.requestPermissionsFromUser(getContext(), [
'ohos.permission.NOTIFICATION_CONTROLLER',
'ohos.permission.VIBRATE'
])
} catch (err) {
console.error('权限申请失败', err)
}
}
// #endif
2.2.2 数据结构设计
闹钟数据结构设计如下:
{
time: '08:00', // 时间
label: '起床', // 标签
repeat: [false, true, ...], // 重复(周日到周六)
ringtone: 0, // 铃声索引
vibrate: true, // 振动
gradualVolume: true, // 音量渐增
snooze: 10, // 贪睡时长(分钟)
enabled: true // 是否启用
}
2.2.3 鸿蒙本地存储实现
uni.storage 在鸿蒙平台会自动使用 Preferences API,提供高性能的键值对存储:
methods: {
// 加载闹钟
loadAlarms() {
const saved = uni.getStorageSync('alarms')
if (saved) {
this.alarms = JSON.parse(saved)
}
},
// 保存闹钟
saveAlarms() {
uni.setStorageSync('alarms', JSON.stringify(this.alarms))
},
// 添加/编辑闹钟
saveAlarm() {
if (this.editIndex >= 0) {
// 编辑现有闹钟
this.alarms[this.editIndex] = this.editAlarmData
} else {
// 添加新闹钟
this.alarms.push(this.editAlarmData)
}
this.saveAlarms()
this.closeDialog()
},
// 删除闹钟
deleteAlarm() {
uni.showModal({
title: '确认删除',
content: '确定要删除这个闹钟吗?',
success: (res) => {
if (res.confirm) {
this.alarms.splice(this.editIndex, 1)
this.saveAlarms()
}
}
})
}
}
2.2.4 鸿蒙通知发送
使用鸿蒙原生通知 API 发送闹钟提醒:
// #ifdef APP-HARMONY
import notificationManager from '@ohos.notificationManager'
// 发送鸿蒙通知
async function sendAlarmNotification(alarmData) {
const notificationRequest = {
id: alarmData.id,
content: {
contentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
normal: {
title: '闹钟提醒',
text: alarmData.label || '该起床了!',
additionalText: new Date().toLocaleTimeString()
}
},
actionButtons: [
{
title: '贪睡',
wantAgent: createSnoozeWantAgent()
},
{
title: '关闭',
wantAgent: createCloseWantAgent()
}
]
}
try {
await notificationManager.publish(notificationRequest)
} catch (err) {
console.error('通知发送失败', err)
}
}
// #endif
// 跨平台兼容写法
function sendNotification(alarmData) {
// #ifdef APP-HARMONY
sendAlarmNotification(alarmData)
// #endif
// #ifndef APP-HARMONY
uni.showModal({
title: '闹钟提醒',
content: alarmData.label || '该起床了!'
})
// #endif
}
2.2.5 鸿蒙振动反馈
// #ifdef APP-HARMONY
import vibrator from '@ohos.vibrator'
// 鸿蒙振动效果
function vibrateHarmony() {
try {
// 预定义振动效果
vibrator.startVibration({
type: 'preset',
effectId: 'haptic.clock.timer',
count: 3
})
// 或自定义振动
vibrator.startVibration({
type: 'time',
duration: 1000
})
} catch (err) {
console.error('振动失败', err)
}
}
// #endif
// 跨平台振动封装
function triggerVibration() {
// #ifdef APP-HARMONY
vibrateHarmony()
// #endif
// #ifndef APP-HARMONY
uni.vibrateShort()
// #endif
}
2.2.6 弹窗表单设计
使用自定义弹窗组件实现闹钟编辑界面:
<view v-if="showDialog" class="dialog-mask" @click="closeDialog">
<view class="dialog" @click.stop>
<view class="dialog-header">
<text class="dialog-title">
{{editIndex >= 0 ? '编辑闹钟' : '添加闹钟'}}
</text>
</view>
<view class="dialog-content">
<!-- 时间选择器 -->
<picker mode="time" :value="editAlarmData.time"
@change="onTimeChange">
<text>{{editAlarmData.time}}</text>
</picker>
<!-- 周重复选择 -->
<view class="week-selector">
<text v-for="(day, i) in weekDays" :key="i"
:class="{'active': editAlarmData.repeat[i]}"
@click="toggleDay(i)">
{{day}}
</text>
</view>
</view>
</view>
</view>
2.3 秒表页面 - 高精度计时与圈数记录
2.3.1 高精度计时实现
使用 10ms 间隔实现百分之一秒精度:
export default {
data() {
return {
isRunning: false,
totalTime: 0, // 总时间(毫秒)
startTime: 0,
timer: null,
laps: [] // 圈数记录
}
},
computed: {
formattedTime() {
const totalSeconds = Math.floor(this.totalTime / 1000)
const minutes = Math.floor(totalSeconds / 60)
const seconds = totalSeconds % 60
return (minutes < 10 ? '0' : '') + minutes + ':' +
(seconds < 10 ? '0' : '') + seconds
},
milliseconds() {
return String(Math.floor((this.totalTime % 1000) / 10))
.padStart(2, '0')
}
},
methods: {
start() {
this.isRunning = true
this.startTime = Date.now() - this.totalTime
this.timer = setInterval(() => {
this.totalTime = Date.now() - this.startTime
}, 10) // 10ms 更新一次
}
}
}
2.3.2 圈数记录与排序
实现圈数记录和多种排序方式:
methods: {
recordLap() {
const lapTime = this.totalTime - this.lastLapTime
this.lapCounter++
this.laps.unshift({
id: Date.now(),
index: this.lapCounter,
time: this.formatTime(lapTime),
rawTime: lapTime
})
this.lastLapTime = this.totalTime
}
},
computed: {
sortedLaps() {
let sorted = [...this.laps]
// 根据排序模式排序
if (this.sortMode === 'fastest') {
sorted.sort((a, b) => a.rawTime - b.rawTime)
} else if (this.sortMode === 'slowest') {
sorted.sort((a, b) => b.rawTime - a.rawTime)
}
// 标记最快和最慢
if (sorted.length > 1) {
const times = this.laps.map(l => l.rawTime)
const fastest = Math.min(...times)
const slowest = Math.max(...times)
sorted = sorted.map(lap => ({
...lap,
isFastest: lap.rawTime === fastest,
isSlowest: lap.rawTime === slowest && fastest !== slowest
}))
}
return sorted
}
}
2.4 计时器页面 - 倒计时与进度显示
2.4.1 时间选择器实现
使用 picker-view 实现时间选择:
<view class="picker-row">
<view class="picker-group">
<picker-view class="picker" :value="[hours]"
@change="onHoursChange">
<picker-view-column>
<view v-for="n in 24" :key="n" class="picker-item">
<text>{{n - 1}}</text>
</view>
</picker-view-column>
</picker-view>
<text class="picker-label">小时</text>
</view>
<!-- 分钟和秒的选择器类似 -->
</view>
2.4.2 进度条实现
实时计算并显示倒计时进度:
computed: {
progressPercent() {
if (this.totalSeconds === 0) return 0
return Math.floor(
(this.totalSeconds - this.remainingTime) /
this.totalSeconds * 100
)
}
}
<view class="progress-bar">
<view class="progress-fill"
:style="{width: progressPercent + '%'}">
</view>
</view>
2.4.3 倒计时完成处理
onTimerComplete() {
this.stopTimer()
this.isRunning = false
// 振动提醒
if (this.vibrate) {
// uni.vibrateLong()
}
// 弹窗通知
if (this.notification) {
uni.showModal({
title: '计时完成',
content: '计时器时间已到!',
showCancel: false
})
}
}
2.5 设置页面 - 丰富的个性化选项
2.5.1 屏幕常亮功能
onKeepScreenOnChange(e) {
this.keepScreenOn = e.detail.value
uni.setStorageSync('keepScreenOn', this.keepScreenOn)
uni.setKeepScreenOn({
keepScreenOn: this.keepScreenOn
})
uni.showToast({
title: this.keepScreenOn ?
'屏幕常亮已开启' : '屏幕常亮已关闭',
icon: 'none'
})
}
2.5.2 数据清除功能
clearData() {
uni.showModal({
title: '确认清除',
content: '此操作将清除所有闹钟和设置数据,无法恢复。确定要继续吗?',
confirmText: '清除',
confirmColor: '#F44336',
success: (res) => {
if (res.confirm) {
uni.clearStorageSync()
uni.showToast({
title: '数据已清除',
icon: 'success'
})
// 重启应用
setTimeout(() => {
uni.reLaunch({
url: '/pages/index/index'
})
}, 1500)
}
}
})
}
三、UI/UX 设计与优化
3.1 Material Design 样式实现
3.1.1 卡片阴影效果
.card {
background-color: #2C2C2C;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}
3.1.2 悬浮按钮(FAB)
.add-btn {
position: fixed;
right: 20px;
bottom: 100px;
width: 60px;
height: 60px;
background-color: #FF6B00;
border-radius: 30px;
display: flex;
justify-content: center;
align-items: center;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}
3.2 颜色系统
深色主题配色方案:
| 元素 | 颜色值 | 用途 |
|---|---|---|
| 主色 | #FF6B00 | 按钮、选中状态、导航栏 |
| 背景 | #212121 | 页面背景 |
| 卡片 | #2C2C2C | 列表项、卡片背景 |
| 分隔 | #3A3A3A | 边框、分隔线 |
| 文本主 | #FFFFFF | 主要文本 |
| 文本次 | #CCCCCC | 次要文本 |
| 文本禁 | #999999 | 禁用文本 |
3.3 交互优化
3.3.1 空状态设计
<view v-if="alarms.length === 0" class="empty-state">
<text class="empty-text">暂无闹钟</text>
<text class="empty-hint">点击下方 "+" 按钮添加闹钟</text>
</view>
3.3.2 加载状态与反馈
// 操作成功提示
uni.showToast({
title: '保存成功',
icon: 'success'
})
// 确认对话框
uni.showModal({
title: '确认删除',
content: '确定要删除这个闹钟吗?',
success: (res) => {
if (res.confirm) {
// 执行删除
}
}
})
四、性能优化
4.1 计时器管理
确保在页面卸载时清除定时器,避免内存泄漏:
export default {
onUnload() {
this.stopTimer()
},
methods: {
stopTimer() {
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
}
}
}
4.2 数据缓存策略
- 使用
uni.setStorageSync同步存储关键数据 - 页面加载时从本地读取数据,减少计算
- 数据变更时立即保存,确保数据一致性
4.3 列表渲染优化
使用 :key 提升列表渲染性能:
<view v-for="(alarm, index) in alarms"
:key="alarm.id || index"
class="alarm-item">
<!-- 内容 -->
</view>
五、鸿蒙平台深度适配
5.1 鸿蒙返回键处理
鸿蒙系统的返回键需要特殊处理,实现双击退出:
// #ifdef APP-HARMONY
let firstBackTime = 0
export default {
onLastPageBackPress: function () {
console.log('HarmonyOS 返回键按下')
if (firstBackTime == 0) {
uni.showToast({
title: '再按一次退出应用',
position: 'bottom',
})
firstBackTime = Date.now()
setTimeout(() => {
firstBackTime = 0
}, 2000)
} else if (Date.now() - firstBackTime < 2000) {
uni.exit() // 退出应用
}
}
}
// #endif
5.2 鸿蒙屏幕常亮
利用鸿蒙的电源管理 API 实现屏幕常亮:
// #ifdef APP-HARMONY
import { runningLock } from '@kit.BasicServicesKit'
let screenLock = null
// 保持屏幕常亮
function keepScreenOn() {
try {
screenLock = runningLock.createRunningLock('screen',
runningLock.RunningLockType.RUNNINGLOCK_SCREEN)
screenLock.lock(0) // 0 表示永久锁定
} catch (err) {
console.error('屏幕常亮失败', err)
}
}
// 释放屏幕锁
function releaseScreenLock() {
if (screenLock) {
screenLock.unlock()
screenLock = null
}
}
// #endif
// 跨平台封装
function setKeepScreenOn(keep) {
// #ifdef APP-HARMONY
if (keep) {
keepScreenOn()
} else {
releaseScreenLock()
}
// #endif
// #ifndef APP-HARMONY
uni.setKeepScreenOn({ keepScreenOn: keep })
// #endif
}
5.3 鸿蒙后台任务
实现闹钟的后台运行:
// #ifdef APP-HARMONY
import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager'
// 申请长时任务
async function requestBackgroundTask() {
try {
await backgroundTaskManager.requestSuspendDelay('时钟后台任务', () => {
console.log('后台任务即将被挂起')
// 保存状态
})
} catch (err) {
console.error('后台任务申请失败', err)
}
}
// #endif
5.4 鸿蒙安全区域适配
考虑鸿蒙设备的刘海屏、水滴屏和折叠屏:
.container {
// 鸿蒙安全区域
padding-top: env(safe-area-inset-top);
padding-bottom: env(safe-area-inset-bottom);
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
}
// 鸿蒙折叠屏适配
@media screen and (min-width: 600px) {
.container {
max-width: 600px;
margin: 0 auto;
}
}
5.5 鸿蒙生命周期适配
export default {
// 应用前台
onShow() {
// #ifdef APP-HARMONY
console.log('HarmonyOS 应用进入前台')
this.resumeTimers() // 恢复计时器
// #endif
},
// 应用后台
onHide() {
// #ifdef APP-HARMONY
console.log('HarmonyOS 应用进入后台')
this.pauseTimers() // 暂停计时器
this.saveState() // 保存状态
// #endif
}
}
5.6 鸿蒙深色模式适配
鸿蒙系统的深色模式可以自动切换:
// #ifdef APP-HARMONY
import { configuration } from '@kit.AbilityKit'
// 监听系统主题变化
function watchThemeChange() {
const config = configuration.getConfiguration()
const isDark = config.colorMode === configuration.ColorMode.COLOR_MODE_DARK
// 应用深色主题
if (isDark) {
this.applyDarkTheme()
} else {
this.applyLightTheme()
}
}
// #endif
六、鸿蒙应用测试与调试
6.1 鸿蒙开发者模式
在鸿蒙设备上启用开发者模式:
- 进入"设置" → "关于手机"
- 连续点击"版本号"7次
- 返回"设置" → "系统和更新" → "开发者选项"
- 开启"USB 调试"和"USB 安装"
6.2 HBuilderX 真机调试
# 1. 连接鸿蒙设备
# 2. 在 HBuilderX 中选择运行设备
# 3. 点击"运行" → "运行到手机或模拟器" → "运行到 HarmonyOS"
# 查看日志
hdc shell hilog | grep "simpleclock"
6.3 鸿蒙特有功能测试清单
- [ ] 通知权限:闹钟通知是否正常显示
- [ ] 振动权限:振动反馈是否生效
- [ ] 后台任务:应用后台时闹钟是否正常工作
- [ ] 屏幕常亮:前台运行时屏幕是否保持常亮
- [ ] 数据持久化:应用重启后数据是否保留
- [ ] 深色模式:系统切换主题时应用是否自动适配
- [ ] 折叠屏适配:在折叠屏设备上布局是否正常
- [ ] 返回键处理:双击返回是否正常退出
6.4 性能测试(鸿蒙平台)
// 使用鸿蒙性能追踪
// #ifdef APP-HARMONY
import hiTraceMeter from '@ohos.hiTraceMeter'
// 开始性能追踪
hiTraceMeter.startTrace('stopwatch_timing', 1001)
// 执行计时逻辑
this.updateStopwatchTime()
// 结束追踪
hiTraceMeter.finishTrace('stopwatch_timing', 1001)
// #endif
6.2 边界情况处理
// 防止重复点击
if (this.totalSeconds === 0) {
uni.showToast({
title: '请设置计时时间',
icon: 'none'
})
return
}
// 数据验证
if (!editAlarmData.time) {
uni.showToast({
title: '请选择时间',
icon: 'none'
})
return
}
七、项目总结
7.1 技术亮点
- 完整的功能闭环:从时间显示到提醒管理,覆盖用户全部需求
- 优雅的代码结构:模块化设计,职责清晰
- 良好的用户体验:Material Design + 深色主题
- 高性能实现:合理使用定时器,优化渲染性能
- 跨平台兼容:一套代码,多端运行
7.2 可扩展方向
- 云同步功能:支持多设备数据同步
- 更多铃声:支持自定义铃声上传
- 统计分析:记录使用习惯,提供数据分析
- 智能提醒:根据用户习惯智能推荐闹钟时间
- 主题定制:支持更多颜色主题
7.3 开发心得
- 用户体验至上:每一个交互细节都要仔细打磨
- 代码质量:保持代码简洁,注重可维护性
- 性能优化:及时清理资源,避免内存泄漏
- 测试驱动:充分测试各种边界情况
- 持续迭代:根据用户反馈不断优化改进
八、源码与资源
8.1 项目结构
simpleclock/
├── pages/
│ ├── index/index.uvue # 264 行
│ ├── alarm/alarm.uvue # 430 行
│ ├── stopwatch/stopwatch.uvue # 280 行
│ ├── timer/timer.uvue # 300 行
│ └── settings/settings.uvue # 350 行
├── App.uvue # 124 行
├── pages.json # 76 行
├── uni.scss # 77 行
├── README.md # 项目说明
└── 使用指南.md # 用户手册
8.2 代码统计
- 总代码量:约 1900+ 行
- 页面数:5 个主要页面
- 组件数:多个自定义组件
- 功能模块:4 个核心功能
8.3 运行环境
- 开发工具:HBuilderX 4.0+
- uni-app x 版本:最新版
- 鸿蒙系统:HarmonyOS 6
- 鸿蒙 SDK:API Level 10+
- 测试设备:
- 华为 Mate 60 系列(HarmonyOS 6)
- 华为 MatePad Pro(折叠屏测试)
- 鸿蒙模拟器
8.4 鸿蒙应用发布
8.4.1 应用签名
# 在华为开发者联盟申请应用签名
# 下载签名文件并配置到 manifest.json
{
"uni-app-x": {
"harmony": {
"signing": {
"profile": "path/to/profile.p7b",
"certFile": "path/to/cert.pem",
"keyFile": "path/to/key.p12",
"keyPassword": "your_password"
}
}
}
}
8.4.2 打包发布
# HBuilderX 中操作:
# 1. 发行 → 原生App-云打包
# 2. 选择 HarmonyOS 平台
# 3. 填写应用信息和签名配置
# 4. 点击打包,等待云端编译
# 5. 下载 .hap 安装包
# 命令行打包(可选)
npm run build:app-harmony
8.4.3 应用上架
- 登录华为开发者联盟
- 进入"应用服务" → "AppGallery Connect"
- 创建应用并上传 .hap 包
- 填写应用信息(截图、描述、分类)
- 提交审核
- 审核通过后上架
九、鸿蒙开发常见问题
9.1 权限被拒绝
问题:应用无法发送通知或振动
解决:
// 在应用启动时主动请求权限
// #ifdef APP-HARMONY
async function checkAndRequestPermissions() {
const permissions = [
'ohos.permission.NOTIFICATION_CONTROLLER',
'ohos.permission.VIBRATE'
]
for (let permission of permissions) {
const result = await checkPermission(permission)
if (!result) {
await requestPermission(permission)
}
}
}
// #endif
9.2 后台任务被终止
问题:闹钟在后台不响
解决:申请长时任务授权,确保应用在后台持续运行
9.3 数据丢失
问题:应用卸载后数据丢失
解决:使用鸿蒙云空间同步数据(需要用户登录华为账号)
9.4 性能问题
问题:页面切换卡顿
解决:
- 使用虚拟列表优化长列表渲染
- 避免在主线程执行耗时操作
- 使用鸿蒙的 Worker 线程处理复杂计算
结语
本文详细介绍了如何使用 uni-app x 开发一款功能完整的鸿蒙6原生时钟应用,从环境搭建、核心功能实现、鸿蒙6平台深度适配到应用发布,涵盖了鸿蒙6应用开发的各个方面。
🎉 这是一个真实的鸿蒙6原生应用开发案例!
核心要点回顾
-
uni-app x 的优势:
- ✅ 编译为鸿蒙原生代码,性能卓越
- ✅ 一套代码多端运行,大幅降低开发成本
- ✅ 丰富的 API 支持,无缝对接鸿蒙能力
-
鸿蒙平台特性:
- ✅ 强大的通知系统
- ✅ 完善的权限管理
- ✅ 优秀的后台任务机制
- ✅ 深度的系统集成
-
开发建议:
- 💡 充分利用条件编译,针对鸿蒙做优化
- 💡 注意权限申请和用户体验
- 💡 重视性能优化和内存管理
- 💡 遵循鸿蒙设计规范
希望这篇文章能帮助你:
- 🎯 零基础入门:从环境搭建到应用发布的完整流程
- 🎨 深度理解鸿蒙6:掌握鸿蒙6的核心特性和设计理念
- 💡 实战经验:学习时间管理类应用的最佳实践
- 🚀 快速上手:30分钟开发出第一个鸿蒙6应用
- 🔥 抓住机遇:在鸿蒙生态爆发期占据先机
下一步计划
- [ ] 适配鸿蒙折叠屏设备
- [ ] 接入华为账号系统
- [ ] 实现华为云空间数据同步
- [ ] 添加鸿蒙小部件(Widget)
- [ ] 接入鸿蒙分布式能力
如果你有任何问题或建议,欢迎交流讨论!让我们一起为鸿蒙生态贡献力量!🚀
关键词:#鸿蒙6 #HarmonyOS6 #uni-app-x #纯血鸿蒙 #原生应用开发 #Vue3 #跨平台开发 #时钟应用
作者:坚果
日期:2025年10月28日
项目地址:GitCode - simpleclock
开源协议:MIT License
📢 开源说明
本项目完全开源,欢迎:
- ⭐ Star 项目,关注后续更新
- 🍴 Fork 项目,二次开发
- 🐛 提交 Issue,反馈问题
- 🤝 提交 PR,贡献代码
当前状态:
- ✅ 核心功能已完成
- ⚠️ 部分细节待优化(如时区同步、铃声播放等)
- 🎯 持续迭代中,欢迎贡献代码!
🔗 相关资源
💬 交流讨论
如果你在开发过程中遇到问题,或有好的想法和建议:
- 💬 欢迎在项目 Issue 区讨论
让我们一起推动鸿蒙生态发展! 🚀🇨🇳
收起阅读 »



































































