写在前面
这是一个用uni-app开发的表情包搜索应用,最大的亮点就是能编译运行到华为鸿蒙系统。说白了,就是用一套代码,可以在iOS、Android、小程序、鸿蒙等多个平台上跑起来。
这个教程是关于这个项目是怎么组织的,用了哪些技术,以及如何编译到鸿蒙系统。
一、整体架构概览
1.1 项目是干啥的?
这个APP就是一个表情包搜索工具,用户可以:
- 浏览热门表情包
- 搜索想要的表情
- 查看专辑分类
- 下载表情包保存到相册
很简单,就是这四大功能。
1.2 技术栈选型
咱们用的技术栈如下:
前端框架
- Vue 3 - 这是uni-app采用的最新版Vue框架
- uni-app - DCloud家的跨平台框架,可以把代码编译到各个平台
- Pinia - Vue 3推荐的状态管理工具(相当于Vuex的升级版)
开发语言
- JavaScript/ES6+ - 主要业务逻辑
- UTS (uni TypeScript) - 用来写鸿蒙原生插件的语言
UI组件
- 原生组件 - 直接用uni-app的内置组件
- 自定义组件 - 比如协议弹窗组件
网络请求
- 基于uni.request封装的HTTP请求库
- Promise风格,支持拦截器
二、项目目录结构
先看看整个项目的目录结构,这样心里有个底:
biaoqingbaosousuogit/
├── common/ # 公共模块
│ ├── api/ # 接口管理
│ │ ├── http.js # 封装的HTTP请求库
│ │ └── base.js # 业务接口定义
│ └── util/ # 工具函数
│ ├── datetime.js # 日期时间处理
│ └── util.js # 通用工具方法
│
├── components/ # 组件库
│ └── agreement-popup/ # 用户协议弹窗组件
│
├── pages/ # 页面目录
│ ├── index/ # 首页(热门表情)
│ ├── album/ # 专辑页
│ ├── search/ # 搜索页
│ ├── detail/ # 详情页
│ └── webview/ # H5页面容器
│
├── stores/ # 状态管理(Pinia)
│ └── main.js # 主store
│
├── static/ # 静态资源
│ ├── icon/ # 图标
│ ├── image/ # 图片
│ └── tabbar/ # 底部导航图标
│
├── uni_modules/ # uni插件模块
│ └── ha-downloadToSystemAlbum/ # 鸿蒙下载插件
│ └── utssdk/
│ └── app-harmony/ # 鸿蒙原生实现
│
├── harmony-configs/ # 鸿蒙配置(重点!)
│ ├── build-profile.json5 # 构建配置、签名证书
│ ├── AppScope/ # 应用级配置
│ └── entry/ # 入口模块配置
│
├── App.vue # 应用入口
├── main.js # 主入口文件
├── pages.json # 页面路由配置
├── manifest.json # 应用配置清单
└── env.js # 环境配置
目录功能说明
common/ - 公共模块,存放各种通用的东西
api/- 网络请求相关,包括封装好的请求库和所有API接口util/- 工具函数,比如日期格式化之类的
pages/ - 存放所有的页面,每个文件夹就是一个页面模块
stores/ - Pinia状态管理,用来存放全局状态数据(比如token、版本号等)
harmony-configs/ - 这个很重要!专门为鸿蒙系统准备的配置文件,包含签名证书、权限声明等
uni_modules/ - uni-app的插件系统,这里有个自定义的鸿蒙下载插件
三、核心模块详解
3.1 网络请求封装(common/api/http.js)
这个文件封装了一个通用的HTTP请求库,解决几个问题:
-
统一的请求配置
- baseUrl配置
- 默认请求头
- 超时时间
-
请求拦截
- 自动添加token
- 自动添加版本号
- 添加平台标识
-
响应处理
- 统一的错误处理
- 日志记录
代码结构大概这样:
export default {
config: {
baseUrl: baseUrl,
timeout: 10000,
// ...其他配置
},
request(options) {
// 从store获取token、版本号等信息
const mainStore = useMainStore();
// 组装请求头
options.header = Object.assign({}, options.header, {
Version: mainStore.version,
Authorization: "Bearer " + mainStore.token,
UniPlatform: mainStore.uniPlatform
});
// 返回Promise
return new Promise((resolve, reject) => {
uni.request({
...options,
complete: (response) => {
if (response.statusCode === 200) {
resolve(response.data);
} else {
reject(response.data);
}
}
});
});
},
get(url, data, options) { /* ... */ },
post(url, data, options) { /* ... */ },
// ...其他方法
}
使用起来很方便:
import api from '@/common/api/base.js'
// 发起请求
const res = await api.homeRandom({ page: 1 })
3.2 业务接口管理(common/api/base.js)
把所有的API接口集中管理,方便维护:
const homeAlbum = (data) => {
return http.request({
url: "/addon/face/home/album",
method: "GET",
data: data,
});
};
const homeRandom = (data) => {
return http.request({
url: "/addon/face/home/random",
method: "GET",
data: data,
});
};
// 导出所有接口
export default {
homeAlbum,
homeRandom,
homeSearch,
homeRelate,
homeAlbumList,
homeDownload,
};
这样做的好处:
- 接口集中管理,一目了然
- 修改接口时只需改这一个地方
- 其他地方import进来直接用
3.3 状态管理(stores/main.js)
用Pinia来管理全局状态,代码很简单:
import { defineStore } from 'pinia';
export const useMainStore = defineStore('main', {
state: () => ({
count: 0,
token: '', // 用户token
version: '', // 应用版本
uniPlatform: '', // 运行平台
}),
actions: {
increment() {
this.count++;
},
},
});
使用方式:
import { useMainStore } from '@/stores/main'
const mainStore = useMainStore()
console.log(mainStore.token) // 读取
mainStore.token = 'xxx' // 写入
比Vuex简单多了,不需要写那么多模板代码。
四、页面实现详解
4.1 首页(pages/index/index.vue)
首页是个瀑布流展示热门表情的页面,核心功能:
-
瀑布流加载
- 首次加载热门表情
- 滚动到底部自动加载更多
-
返回顶部
- 滚动超过500px显示返回顶部按钮
-
用户协议
- 首次打开显示用户协议弹窗
关键代码片段:
// 获取热门表情
const getRandomEmojis = async (isLoadMore = false) => {
if (loading.value || !hasMore.value) return;
loading.value = true;
try {
const res = await api.homeRandom({ page: page.value });
if (res.code === 200) {
if (isLoadMore) {
emojis.value = [...emojis.value, ...res.data];
} else {
emojis.value = res.data;
}
hasMore.value = res.data.length > 0;
if (hasMore.value) {
page.value++;
}
}
} catch (e) {
console.error('获取热门表情失败:', e);
} finally {
loading.value = false;
}
};
// 页面触底加载更多
onReachBottom(() => {
getRandomEmojis(true);
});
技巧点:
- 用
loading和hasMore标记来避免重复请求 - 用
isLoadMore参数区分首次加载和追加加载 onReachBottom是uni-app提供的触底钩子
4.2 详情页(pages/detail/detail.vue)
详情页展示单个表情包,可以下载保存,还会推荐相关的表情。
关键功能:
-
接收参数
onLoad((options) => { if (options.item) { emojiData.value = JSON.parse(decodeURIComponent(options.item)) } }) -
下载保存
const saveImage = () => { uni.downloadFile({ url: emojiData.value.imgurl, success: (res) => { if (res.statusCode === 200) { uni.saveImageToPhotosAlbum({ filePath: res.tempFilePath, success: () => { uni.showToast({ title: '保存成功', icon: 'success' }) } }) } } }) } -
相关推荐
- 调用
api.homeRelate获取相关表情 - 点击推荐项时切换当前表情并刷新推荐列表
- 调用
五、鸿蒙适配核心技术
这部分是重点中的重点!如何让uni-app编译到鸿蒙系统。
5.1 鸿蒙配置目录(harmony-configs/)
这个目录是专门为鸿蒙准备的,uni-app在编译鸿蒙版本时会读取这些配置。
目录结构:
harmony-configs/
├── build-profile.json5 # 构建配置
├── AppScope/
│ ├── app.json5 # 应用信息
│ └── resources/ # 应用资源(图标等)
└── entry/
└── src/main/
└── module.json5 # 模块配置(权限等)
5.2 构建配置(build-profile.json5)
这个文件配置签名证书和SDK版本,非常重要:
{
"app": {
"signingConfigs": [
{
"name": "default",
"type": "HarmonyOS",
"material": {
"storePassword": "你的证书库密码",
"certpath": "你的证书文件路径",
"keyAlias": "密钥别名",
"keyPassword": "密钥密码",
"profile": "配置文件路径",
"signAlg": "SHA256withECDSA",
"storeFile": "证书库文件路径"
}
}
],
"products": [
{
"name": "default",
"signingConfig": "default",
"compatibleSdkVersion": "5.0.1(13)", // SDK版本
"runtimeOS": "HarmonyOS"
}
]
}
}
重点:
signingConfigs- 配置开发和发布证书compatibleSdkVersion- 兼容的鸿蒙SDK版本- 证书文件需要从华为AGC控制台申请
5.3 应用配置(AppScope/app.json5)
配置应用的基本信息:
{
"app": {
"bundleName": "com.letwind.biaoqingbaozhushou", // 包名
"vendor": "letwind",
"versionCode": 101,
"versionName": "1.0.1",
"icon": "$media:app_icon",
"label": "$string:app_name"
}
}
5.4 模块配置(entry/src/main/module.json5)
配置应用权限和能力:
{
"module": {
"name": "entry",
"type": "entry",
"deviceTypes": ["phone"],
"requestPermissions": [
{
"name": "ohos.permission.INTERNET" // 网络权限
}
]
}
}
5.5 manifest.json中的鸿蒙配置
在uni-app的manifest.json里也要配置鸿蒙相关信息:
{
"vueVersion": "3",
"app-harmony": {
"distribute": {
"bundleName": "com.letwind.biaoqingbaozhushou",
"icons": {
"foreground": "static/icon/logo1024.png",
"background": "static/icon/logo1024.png"
}
}
}
}
这里的bundleName要和app.json5里的保持一致!
六、编译运行到鸿蒙
6.1 环境准备
-
安装HBuilderX
- 去DCloud官网下载最新版HBuilderX
- 必须是支持鸿蒙的版本
-
准备证书
- 去华为AGC控制台申请证书
6.2 配置证书
编辑harmony-configs/build-profile.json5:
{
"app": {
"signingConfigs": [
{
"name": "default",
"material": {
"storePassword": "实际的证书库密码",
"certpath": "证书文件绝对路径/cert.cer",
"keyAlias": "实际的密钥别名",
"keyPassword": "实际的密钥密码",
"profile": "配置文件绝对路径/profile.p7b",
"signAlg": "SHA256withECDSA",
"storeFile": "证书库文件绝对路径/cert.p12"
}
}
]
}
}
6.3 运行项目
- 在HBuilderX中打开项目
- 点击工具栏的"运行"
- 选择"运行到鸿蒙"
- 选择设备
- 等待编译完成,APP会自动安装到设备上
6.4 打包发布
-
配置发布证书
- 在
signingConfigs中配置release证书 - 发布证书要在AGC控制台单独申请
- 在
-
本地打包
- 点击HBuilderX的"发行"菜单
- 选择"鸿蒙本地打包"
- 生成.app文件
-
上架应用市场
- 登录华为应用市场控制台
- 上传.app文件
- 填写应用信息
- 提交审核
七、常见问题和解决方案
7.1 证书配置错误
问题: 编译时报错"签名失败"
解决:
- 确认证书文件路径正确(建议用绝对路径)
- 检查证书密码是否正确
- 确认证书和profile文件是匹配的
- 检查bundleName是否和证书中的包名一致
7.2 设备识别不了
问题: HBuilderX检测不到鸿蒙设备
解决:
- 确认手机已开启开发者模式和USB调试
- 更换数据线(有些数据线只能充电不能传输数据)
- 重启HBuilderX和手机
- 检查是否安装了华为手机驱动
7.3 页面跳转失败
问题: 在鸿蒙上页面跳转不成功
解决:
- 检查
pages.json中是否已注册该页面 - 检查跳转路径是否正确(不要带.vue后缀)
- 如果是tabBar页面,要用
uni.switchTab而不是uni.navigateTo
八、项目亮点总结
8.1 跨平台能力
一套代码,多端运行:
- iOS APP
- Android APP
- 鸿蒙APP(HarmonyOS NEXT)
- 微信小程序
- H5网页
这就是uni-app的最大价值,开发效率极高。
8.2 鸿蒙适配方案
完整的鸿蒙适配解决方案:
- 标准的配置文件结构
- 签名证书管理
- 系统API调用(相册、下载等)
可以作为其他uni-app项目适配鸿蒙的参考模板。
8.3 工程化实践
规范的项目结构:
- 清晰的目录划分
- API集中管理
- 组件化开发
- 状态统一管理
代码可维护性强,方便团队协作。
写在最后
这个项目虽然功能不算复杂,但麻雀虽小五脏俱全,把uni-app开发鸿蒙应用的整套流程都走通了。
关键点就这几个:
- uni-app框架 - 跨平台的基础
- harmony-configs - 鸿蒙配置的核心
- 证书签名 - 打包发布的关键
只要搞懂这几个点,你也能把自己的uni-app项目跑在鸿蒙上。
如果遇到问题,多看看官方文档,或者加入开发者社群交流。鸿蒙生态还在快速发展,遇到坑是正常的,解决问题的过程也是成长的过程。
加油!🚀
项目开源地址
GitHub: https://github.com/zwpro/uniapptohongmeng
欢迎 Star、Fork 和提交 Issue!