【鸿蒙征文】从现在起,你的非原生弹窗“组件”们(自定义Toast、Modal等)只需要配置一次!
介绍
👋 hello, 您那要是早上,那祝你早安。要是下午,那祝你午安。晚上看?那还是别看了,点个赞,明天再看
我是 skiyee 是本篇的作者,常活跃在 uni-app 生态领域
随着 Harmony 不断增强以及大力推广,更多的应用场景进入我们的眼帘
我就在想,要是 uni-app 也适配了 Harmony,那不就省事了,一键多端共同开发,不再需要花费更多的学习成本
您猜怎么着,uni-app 还真适配 Harmony Next 了,那么我们就可以结合 uni-app 原本的生态来开发了!
痛点
很多朋友在编写弹窗时,觉得原生的鸿蒙弹窗不好看,就想自定义一个美美滴。但发现,uni-app 中居然没有地方能够一键进行设置,全局就可以调用的
朋友们所期望的应该是像体验原生一般,如 uni.showToast({...}) 这种,在任何地方都能调起的
为了解决这个痛点,我结合 uni-app 使用的底层构建工具 vite 开发了一个模拟根组件能力的组件!@uni-ku/root
开始
安装
在我们的 HBuilder 中,选择我们的项目打开命令行窗口,输入命令
npm install -D @uni-ku/root
配置
在 vite.config.js 中引入 @uni-ku/root
// vite.config.js
import Uni from '@dcloudio/vite-plugin-uni'
import UniKuRoot from '@uni-ku/root'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
// 文档: https://github.com/uni-ku/root
UniKuRoot(),
Uni()
]
})
使用
创建关键 App.ku.vue 文件,并通过标签 <KuRootView /> 或 <ku-root-view /> 指定视图存放位置
<script setup>
import { ref } from 'vue'
const UniKuRoot = ref('Hello UniKu Root')
</script>
<template>
<div>{{ UniKuRoot }}</div>
<!-- 视图存放位置 -->
<KuRootView />
</template>
封装
编写自定义的 Toast 组件
<!-- components/GlobalToast.vue -->
<script setup>
import { useToast } from '@/composables/useToast'
const { globalToastState, hideToast } = useToast()
</script>
<template>
<div v-if="globalToastState" class="toast-wrapper" @click="hideToast">
<div class="toast-box">
welcome to use @uni-ku/root
</div>
</div>
</template>
<style scoped>
.toast-wrapper{
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
}
.toast-box{
background: white;
color: black;
}
</style>
实现 Toast 调起方法
// composables/useToast.js
import { ref } from 'vue'
const globalToastState = ref(false)
export function useToast() {
function showToast() {
globalToastState.value = true
}
function hideToast() {
globalToastState.value = false
}
return {
globalToastState,
showToast,
hideToast,
}
}
挂载至 App.ku.vue
<!-- App.ku.vue -->
<script setup>
import GlobalToast from '@/components/GlobalToast.vue'
</script>
<template>
<KuRootView />
<GlobalToast />
</template>
视图内部触发 Toast 组件
<!-- pages/index(或者其他页面).vue -->
<script setup lang="ts">
import { useToast } from '@/composables/useToast'
const { showToast } = useToast()
</script>
<template>
<view>
Hello UniKuRoot
</view>
<button @click="showToast">
视图内触发展示Toast
</button>
</template>
具体代码详见:示例
最终效果
打开我们的鸿蒙应用,来展示我们的最终效果
上面这个Toast只是一个示例,最终需要利用 UniKuRoot 去实现什么效果,是开发者自行可以想象的!
结语
结语就说些心里话吧,随着对 uni-app 的越来越多的理解,我觉得目前 uni-app 是跨平台小程序做的最棒的、App跨端方面是生态最好的。
虽然有些功能还不是很棒,但是目前仍在不断的更新迭代、不断的完善,我相信有开发人员和生态建设的爱好者们的参与会越来越棒!
鼓励
如果为此感到兴奋,可以通过以下渠道让我们知道!
生态
以下都是关于 UniApp 生态相关的仓库
介绍
👋 hello, 您那要是早上,那祝你早安。要是下午,那祝你午安。晚上看?那还是别看了,点个赞,明天再看
我是 skiyee 是本篇的作者,常活跃在 uni-app 生态领域
随着 Harmony 不断增强以及大力推广,更多的应用场景进入我们的眼帘
我就在想,要是 uni-app 也适配了 Harmony,那不就省事了,一键多端共同开发,不再需要花费更多的学习成本
您猜怎么着,uni-app 还真适配 Harmony Next 了,那么我们就可以结合 uni-app 原本的生态来开发了!
痛点
很多朋友在编写弹窗时,觉得原生的鸿蒙弹窗不好看,就想自定义一个美美滴。但发现,uni-app 中居然没有地方能够一键进行设置,全局就可以调用的
朋友们所期望的应该是像体验原生一般,如 uni.showToast({...}) 这种,在任何地方都能调起的
为了解决这个痛点,我结合 uni-app 使用的底层构建工具 vite 开发了一个模拟根组件能力的组件!@uni-ku/root
开始
安装
在我们的 HBuilder 中,选择我们的项目打开命令行窗口,输入命令
npm install -D @uni-ku/root
配置
在 vite.config.js 中引入 @uni-ku/root
// vite.config.js
import Uni from '@dcloudio/vite-plugin-uni'
import UniKuRoot from '@uni-ku/root'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
// 文档: https://github.com/uni-ku/root
UniKuRoot(),
Uni()
]
})
使用
创建关键 App.ku.vue 文件,并通过标签 <KuRootView /> 或 <ku-root-view /> 指定视图存放位置
<script setup>
import { ref } from 'vue'
const UniKuRoot = ref('Hello UniKu Root')
</script>
<template>
<div>{{ UniKuRoot }}</div>
<!-- 视图存放位置 -->
<KuRootView />
</template>
封装
编写自定义的 Toast 组件
<!-- components/GlobalToast.vue -->
<script setup>
import { useToast } from '@/composables/useToast'
const { globalToastState, hideToast } = useToast()
</script>
<template>
<div v-if="globalToastState" class="toast-wrapper" @click="hideToast">
<div class="toast-box">
welcome to use @uni-ku/root
</div>
</div>
</template>
<style scoped>
.toast-wrapper{
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
}
.toast-box{
background: white;
color: black;
}
</style>
实现 Toast 调起方法
// composables/useToast.js
import { ref } from 'vue'
const globalToastState = ref(false)
export function useToast() {
function showToast() {
globalToastState.value = true
}
function hideToast() {
globalToastState.value = false
}
return {
globalToastState,
showToast,
hideToast,
}
}
挂载至 App.ku.vue
<!-- App.ku.vue -->
<script setup>
import GlobalToast from '@/components/GlobalToast.vue'
</script>
<template>
<KuRootView />
<GlobalToast />
</template>
视图内部触发 Toast 组件
<!-- pages/index(或者其他页面).vue -->
<script setup lang="ts">
import { useToast } from '@/composables/useToast'
const { showToast } = useToast()
</script>
<template>
<view>
Hello UniKuRoot
</view>
<button @click="showToast">
视图内触发展示Toast
</button>
</template>
具体代码详见:示例
最终效果
打开我们的鸿蒙应用,来展示我们的最终效果
上面这个Toast只是一个示例,最终需要利用 UniKuRoot 去实现什么效果,是开发者自行可以想象的!
结语
结语就说些心里话吧,随着对 uni-app 的越来越多的理解,我觉得目前 uni-app 是跨平台小程序做的最棒的、App跨端方面是生态最好的。
虽然有些功能还不是很棒,但是目前仍在不断的更新迭代、不断的完善,我相信有开发人员和生态建设的爱好者们的参与会越来越棒!
鼓励
如果为此感到兴奋,可以通过以下渠道让我们知道!
生态
以下都是关于 UniApp 生态相关的仓库
收起阅读 »uniapp app 端如何可以监听到声音的分贝值?我现在用的是uni.getRecorderManager() 这个方法,不支持监听声音的分贝值。
我现在想实现一个功能,实时的去监听用户讲话,想如果用户的声音的大小就识别客户已经讲完话,从而去实现对应的逻辑。有什么办法可以实现吗?麻烦各位大神指导一下,谢谢!
我现在想实现一个功能,实时的去监听用户讲话,想如果用户的声音的大小就识别客户已经讲完话,从而去实现对应的逻辑。有什么办法可以实现吗?麻烦各位大神指导一下,谢谢!
我用tmx-iu开发了一个拍日出的app
大家好,分享一个用tmx-ui做的鸿蒙App - Aura(日落色彩记录工具)。
📱 App简介
自动提取照片主色生成渐变色卡。说实话老婆一开始还嘲笑我"还不如做个敲木鱼的"😂,但功能还是挺实用的。
核心功能:
- K-means聚类算法提取主色(5色渐变)
- 8种分享模板(Canvas渲染)
- 900+中国古典色库
欢迎下载体验 ➡️ https://appgallery.huawei.com/app/detail?id=auar0.0.1&channelId=SHARE&source=appshare
🎨 关于组件库
说实话,用tmx-ui组件库开发效率真的高。10天时间我做了2个App,Aura是其中一个。
组件库帮我省了大量时间:
x-sheet快速搭建布局结构x-navbar导航栏开箱即用x-button、x-icon这些基础组件样式统一- 类型定义特别完善,开发时智能提示很舒服
- 主题配置系统省去了大量颜色适配工作
大家好,分享一个用tmx-ui做的鸿蒙App - Aura(日落色彩记录工具)。
📱 App简介
自动提取照片主色生成渐变色卡。说实话老婆一开始还嘲笑我"还不如做个敲木鱼的"😂,但功能还是挺实用的。
核心功能:
- K-means聚类算法提取主色(5色渐变)
- 8种分享模板(Canvas渲染)
- 900+中国古典色库
欢迎下载体验 ➡️ https://appgallery.huawei.com/app/detail?id=auar0.0.1&channelId=SHARE&source=appshare
🎨 关于组件库
说实话,用tmx-ui组件库开发效率真的高。10天时间我做了2个App,Aura是其中一个。
组件库帮我省了大量时间:
x-sheet快速搭建布局结构x-navbar导航栏开箱即用x-button、x-icon这些基础组件样式统一- 类型定义特别完善,开发时智能提示很舒服
- 主题配置系统省去了大量颜色适配工作
cli关于pnpm安装vue-i18n的问题
直接pnpm i之后会提示类似这种错误
试了改版本或者删nodemodule都不行,据gemini说是pnpm的解析依赖导致的,然后要了个办法解决了
"dependencies": {
"vue-i18n": "9.14.5",
},
首先看你这里的版本号是多少,然后去仓库直接翻源码找到这个版本的包的package,
看这里依赖的版本是多少,然后在自己的依赖里加上这个强制pnpm去安装这几个版本就解决了
"pnpm": {
"overrides": {
"@vue/devtools-api": "^6.5.0",
"@intlify/core-base": "9.14.5",
"@intlify/shared": "9.14.5",
"@intlify/devtools-if": "9.14.5",
"@intlify/vue-devtools": "9.14.5"
}
},
补充------------,这样解决了h5运行,小程序似乎又会出现新问题
补充------------,原来把i18n降一下版本就没事了
直接pnpm i之后会提示类似这种错误
试了改版本或者删nodemodule都不行,据gemini说是pnpm的解析依赖导致的,然后要了个办法解决了
"dependencies": {
"vue-i18n": "9.14.5",
},
首先看你这里的版本号是多少,然后去仓库直接翻源码找到这个版本的包的package,
看这里依赖的版本是多少,然后在自己的依赖里加上这个强制pnpm去安装这几个版本就解决了
"pnpm": {
"overrides": {
"@vue/devtools-api": "^6.5.0",
"@intlify/core-base": "9.14.5",
"@intlify/shared": "9.14.5",
"@intlify/devtools-if": "9.14.5",
"@intlify/vue-devtools": "9.14.5"
}
},
补充------------,这样解决了h5运行,小程序似乎又会出现新问题
补充------------,原来把i18n降一下版本就没事了 收起阅读 »
【鸿蒙征文】Uni ECharts 2.1 发布:正式支持鸿蒙,零成本迁移、全平台兼容、跨端开发零负担!
Uni ECharts 是适用于 uni-app 的 Apache ECharts 组件,无需繁琐的步骤即可轻松在 uni-app 平台上使用 echarts。
官网 & 文档:https://uni-echarts.xiaohe.ink
插件市场:https://ext.dcloud.net.cn/plugin?id=22035
Github:https://github.com/xiaohe0601/uni-echarts
🏝️ 背景
🎵 “本来应该从从容容游刃有余,现在是匆匆忙忙连滚带爬,睁眼说瞎话,你在哽咽什么啦,你在哭什么哭,没出息!”
每当听见同事阿尹在工位旁哼起这首歌,我都忍不住陷入沉思 —— 那一刻,我看到的不只是他在 emo,更像是无数开发者在鸿蒙适配路上的缩影。
是的,在过去一段时间里,由于 uni-app 不支持鸿蒙模拟器调试,而我又苦于没有鸿蒙手机,导致 Uni ECharts 并不能在鸿蒙系统上顺利运行。有鸿蒙需求的开发者们用起来就像是在赶末班车 “匆匆忙忙、连滚带爬”,我是夜不能寐、如鲠在喉。
如今 uni-app 终于支持鸿蒙模拟器调试,痛定思痛,我再也坐不住了!这一次,一定要让这件事情画上一个完美的句号。
于是,我们决定不再将就,团队成员一拍即合 —— 必须让 Uni ECharts 能够在鸿蒙系统运行,与主流生态全面接轨。更重要的是,无需改动一行代码,真正做到 “一次开发、多端运行”,开发者从此 “从从容容、游刃有余”,不再哽咽,大家都会 “有出息”!
上文中的 “团队成员” 目前指的是我自己 🙃,如果你对维护 Uni ECharts 感兴趣的话欢迎到 Github 提交 PR 👏,一起用爱发电!
在此,对已经或将来为 Uni ECharts 贡献代码的开发者朋友们由衷表示感谢!🙏
项目地址:https://github.com/xiaohe0601/uni-echarts
🎉 2.1 正式发布
现在,Uni ECharts 成功完成了对鸿蒙的适配,所以 2.1 版本正式发布啦!
安装及使用方法与其他端别无二致,那么就一起来回顾一下吧 ~
👉 前往 Uni ECharts 官网 快速开始 查看完整内容
前置条件:
- echarts >= 5.3.0
- vue >= 3.3.0(目前 uni-app 尚未适配 Vue 3.5,推荐使用
3.4.x与 uni-app 保持一致)
安装
# pnpm
pnpm add echarts uni-echarts
# yarn
yarn add echarts uni-echarts
# npm
npm install echarts uni-echarts
配置
由于 Uni ECharts 发布到 npm 上的包是未经编译的 vue 文件,为了避免 Vite 对 Uni ECharts 依赖预构建 导致生成额外的 echarts 副本,当使用 npm 方式时需要手动配置 Vite 强制排除 uni-echarts 的预构建。
// vite.config.js[ts]
import { defineConfig } from "vite";
export default defineConfig({
// ...
optimizeDeps: {
exclude: [
"uni-echarts"
]
}
});
Vite 插件
自 2.0.0 开始,Uni ECharts 提供了 Vite 插件用于自动化处理一些繁琐、重复的工作,也为将来更多的高级功能提供了可能性。
// vite.config.js[ts]
import { UniEcharts } from "uni-echarts/vite";
import { defineConfig } from "vite";
export default defineConfig({
// ...
plugins: [
UniEcharts()
]
});
自动导入(可选)
Uni ECharts 可以配合 @uni-helper/vite-plugin-uni-components 和 unplugin-auto-import 实现组件和 API 的自动按需导入。
# pnpm
pnpm add -D @uni-helper/vite-plugin-uni-components unplugin-auto-import
# yarn
yarn add --dev @uni-helper/vite-plugin-uni-components unplugin-auto-import
# npm
npm install -D @uni-helper/vite-plugin-uni-components unplugin-auto-import
// vite.config.js[ts]
import Uni from "@dcloudio/vite-plugin-uni";
import UniComponents from "@uni-helper/vite-plugin-uni-components";
import { UniEchartsResolver } from "uni-echarts/resolver";
import AutoImport from "unplugin-auto-import/vite";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [
AutoImport({
resolvers: [
UniEchartsResolver()
]
}),
// 确保放在 `Uni()` 之前
UniComponents({
resolvers: [
UniEchartsResolver()
]
}),
Uni()
]
});
如果使用 pnpm 管理依赖,请在项目根目录下的 .npmrc 文件中添加如下内容,参见 issue 389。
shamefully-hoist=true # or public-hoist-pattern[]=@vue*
如果使用 TypeScript 可以在 tsconfig.json 中添加如下内容为自动导入的组件提供类型提示(需要 IDE 支持)。
{
"compilerOptions": {
"types": [
// ...
"uni-echarts/global"
]
}
}
使用
<template>
<uni-echarts custom-class="chart" :option="option"></uni-echarts>
</template>
<script setup>
import { PieChart } from "echarts/charts";
import { DatasetComponent, LegendComponent, TooltipComponent } from "echarts/components";
import * as echarts from "echarts/core";
import { CanvasRenderer } from "echarts/renderers";
import { ref } from "vue";
echarts.use([
LegendComponent,
TooltipComponent,
DatasetComponent,
PieChart,
CanvasRenderer
]);
const option = ref({
legend: {
top: 10,
left: "center"
},
tooltip: {
trigger: "item",
textStyle: {
// #ifdef MP-WEIXIN
// 临时解决微信小程序 tooltip 文字阴影问题
textShadowBlur: 1
// #endif
}
},
series: [
{
type: "pie",
radius: ["30%", "52%"],
label: {
show: false,
position: "center"
},
itemStyle: {
borderWidth: 2,
borderColor: "#ffffff",
borderRadius: 10
},
emphasis: {
label: {
show: true,
fontSize: 20
}
}
}
],
dataset: {
dimensions: ["来源", "数量"],
source: [
["Search Engine", 1048],
["Direct", 735],
["Email", 580],
["Union Ads", 484],
["Video Ads", 300]
]
}
});
</script>
<style scoped>
.chart {
height: 300px;
}
</style>
小程序端图表不显示?
请参考常见问题中 小程序端 class / style 无效 部分的说明。
❤️ 支持 & 鼓励
如果 Uni ECharts 对你有帮助,可以通过以下渠道对我们表示鼓励:
无论 ⭐️ 还是 💰 支持,我们铭记于心,这将是我们继续前进的动力,感谢您的支持!
Uni ECharts 是适用于 uni-app 的 Apache ECharts 组件,无需繁琐的步骤即可轻松在 uni-app 平台上使用 echarts。
官网 & 文档:https://uni-echarts.xiaohe.ink
插件市场:https://ext.dcloud.net.cn/plugin?id=22035
Github:https://github.com/xiaohe0601/uni-echarts
🏝️ 背景
🎵 “本来应该从从容容游刃有余,现在是匆匆忙忙连滚带爬,睁眼说瞎话,你在哽咽什么啦,你在哭什么哭,没出息!”
每当听见同事阿尹在工位旁哼起这首歌,我都忍不住陷入沉思 —— 那一刻,我看到的不只是他在 emo,更像是无数开发者在鸿蒙适配路上的缩影。
是的,在过去一段时间里,由于 uni-app 不支持鸿蒙模拟器调试,而我又苦于没有鸿蒙手机,导致 Uni ECharts 并不能在鸿蒙系统上顺利运行。有鸿蒙需求的开发者们用起来就像是在赶末班车 “匆匆忙忙、连滚带爬”,我是夜不能寐、如鲠在喉。
如今 uni-app 终于支持鸿蒙模拟器调试,痛定思痛,我再也坐不住了!这一次,一定要让这件事情画上一个完美的句号。
于是,我们决定不再将就,团队成员一拍即合 —— 必须让 Uni ECharts 能够在鸿蒙系统运行,与主流生态全面接轨。更重要的是,无需改动一行代码,真正做到 “一次开发、多端运行”,开发者从此 “从从容容、游刃有余”,不再哽咽,大家都会 “有出息”!
上文中的 “团队成员” 目前指的是我自己 🙃,如果你对维护 Uni ECharts 感兴趣的话欢迎到 Github 提交 PR 👏,一起用爱发电!
在此,对已经或将来为 Uni ECharts 贡献代码的开发者朋友们由衷表示感谢!🙏
项目地址:https://github.com/xiaohe0601/uni-echarts
🎉 2.1 正式发布
现在,Uni ECharts 成功完成了对鸿蒙的适配,所以 2.1 版本正式发布啦!
安装及使用方法与其他端别无二致,那么就一起来回顾一下吧 ~
👉 前往 Uni ECharts 官网 快速开始 查看完整内容
前置条件:
- echarts >= 5.3.0
- vue >= 3.3.0(目前 uni-app 尚未适配 Vue 3.5,推荐使用
3.4.x与 uni-app 保持一致)
安装
# pnpm
pnpm add echarts uni-echarts
# yarn
yarn add echarts uni-echarts
# npm
npm install echarts uni-echarts
配置
由于 Uni ECharts 发布到 npm 上的包是未经编译的 vue 文件,为了避免 Vite 对 Uni ECharts 依赖预构建 导致生成额外的 echarts 副本,当使用 npm 方式时需要手动配置 Vite 强制排除 uni-echarts 的预构建。
// vite.config.js[ts]
import { defineConfig } from "vite";
export default defineConfig({
// ...
optimizeDeps: {
exclude: [
"uni-echarts"
]
}
});
Vite 插件
自 2.0.0 开始,Uni ECharts 提供了 Vite 插件用于自动化处理一些繁琐、重复的工作,也为将来更多的高级功能提供了可能性。
// vite.config.js[ts]
import { UniEcharts } from "uni-echarts/vite";
import { defineConfig } from "vite";
export default defineConfig({
// ...
plugins: [
UniEcharts()
]
});
自动导入(可选)
Uni ECharts 可以配合 @uni-helper/vite-plugin-uni-components 和 unplugin-auto-import 实现组件和 API 的自动按需导入。
# pnpm
pnpm add -D @uni-helper/vite-plugin-uni-components unplugin-auto-import
# yarn
yarn add --dev @uni-helper/vite-plugin-uni-components unplugin-auto-import
# npm
npm install -D @uni-helper/vite-plugin-uni-components unplugin-auto-import
// vite.config.js[ts]
import Uni from "@dcloudio/vite-plugin-uni";
import UniComponents from "@uni-helper/vite-plugin-uni-components";
import { UniEchartsResolver } from "uni-echarts/resolver";
import AutoImport from "unplugin-auto-import/vite";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [
AutoImport({
resolvers: [
UniEchartsResolver()
]
}),
// 确保放在 `Uni()` 之前
UniComponents({
resolvers: [
UniEchartsResolver()
]
}),
Uni()
]
});
如果使用 pnpm 管理依赖,请在项目根目录下的 .npmrc 文件中添加如下内容,参见 issue 389。
shamefully-hoist=true # or public-hoist-pattern[]=@vue*
如果使用 TypeScript 可以在 tsconfig.json 中添加如下内容为自动导入的组件提供类型提示(需要 IDE 支持)。
{
"compilerOptions": {
"types": [
// ...
"uni-echarts/global"
]
}
}
使用
<template>
<uni-echarts custom-class="chart" :option="option"></uni-echarts>
</template>
<script setup>
import { PieChart } from "echarts/charts";
import { DatasetComponent, LegendComponent, TooltipComponent } from "echarts/components";
import * as echarts from "echarts/core";
import { CanvasRenderer } from "echarts/renderers";
import { ref } from "vue";
echarts.use([
LegendComponent,
TooltipComponent,
DatasetComponent,
PieChart,
CanvasRenderer
]);
const option = ref({
legend: {
top: 10,
left: "center"
},
tooltip: {
trigger: "item",
textStyle: {
// #ifdef MP-WEIXIN
// 临时解决微信小程序 tooltip 文字阴影问题
textShadowBlur: 1
// #endif
}
},
series: [
{
type: "pie",
radius: ["30%", "52%"],
label: {
show: false,
position: "center"
},
itemStyle: {
borderWidth: 2,
borderColor: "#ffffff",
borderRadius: 10
},
emphasis: {
label: {
show: true,
fontSize: 20
}
}
}
],
dataset: {
dimensions: ["来源", "数量"],
source: [
["Search Engine", 1048],
["Direct", 735],
["Email", 580],
["Union Ads", 484],
["Video Ads", 300]
]
}
});
</script>
<style scoped>
.chart {
height: 300px;
}
</style>
小程序端图表不显示?
请参考常见问题中 小程序端 class / style 无效 部分的说明。
❤️ 支持 & 鼓励
如果 Uni ECharts 对你有帮助,可以通过以下渠道对我们表示鼓励:
无论 ⭐️ 还是 💰 支持,我们铭记于心,这将是我们继续前进的动力,感谢您的支持!
收起阅读 »从新建文件夹开始,用一个项目实战讲透uni-app鸿蒙开发到上架全流程(多图、细节)
零、 前言
用一个实战出发,结合自身从开发到上架的全过程经验,尽可能细致地描述每一个“关键步骤”。不管是刚接触鸿蒙开发的新手,还是已有经验的开发者,都希望能从本篇文章中受益。
-
需求制定
要开发一个应用,首先要明白自己想要做什么。这包括确定应用的功能、用户需求等。只有明确了需求,开发应用才能有方向。可以参考多款竞品,也可以发给AI分析应用功能,如:我想要开发一款专注于健康饮食的应用,提供丰富的营养成分查询、多种健康计算工具、个性化食谱推荐等功能,帮助用户科学管理饮食健康。这时候就可以把需求发给AI帮忙分析落地功能 -
技术选型
uni-app 对鸿蒙的良好支持,选择使用 uni-app + Vue 3 作为开发框架。同时利用uni-ui官方组件库,快速搭建应用界面。一、开发前的准备
在这一章节中,将讲述使用 uni-app 开发鸿蒙 App 的前置工作,供开发者参考。
1.1 开发环境的搭建
软件环境
- 操作系统:Windows 10
- 浏览器:版本 142.0.7444.60(正式版,64 位)
说明:虽然是开发鸿蒙 App,但在一些样式调试或隐性数据操作中,先使用浏览器能更快定位问题。在 uni-app 的加持下,浏览器中的表现与鸿蒙上基本一致(除部分鸿蒙特性外)。
-
HBuilder X:4.76(截至发稿我已升级至 4.84,适配了很多鸿蒙相关更新,未更新的同学请及时更新)
-
DevEco Studio:5.1.1 Beta(实战项目环境)/ 6.0.0 Release(新项目推荐)
兼容性参考:官方文档(点此前往)
下载地址:DevEco Studio 下载(点此前往)
1.2 技术栈
- 前端框架:uni-app + Vue 3(Composition API,`<script setup>`)
- UI 组件:uni-ui
- 数据存储:Pinia([参考文档](https://uniapp.dcloud.net.cn/tutorial/vue3-pinia.html#%E7%8A%B6%E6%80%81%E7%AE%A1%E7%90%86-pinia))
1.3 鸿蒙环境及模拟器
- 打开 DevEco Studio 并创建空白项目
- 点击【工具】→【设备管理器】选择一个
API 19及以上的设备下载并安装
二、项目创建
2.1 创建项目
- 打开 HBuilder X:点击左上角【文件】→【新建】→【uni-app】→【uni-app 项目】
- 填写项目名称(建议英文或数字,不要使用中文)
- 选择框架:Vue 3(默认)
- 点击【完成】,创建项目
注意:
- 项目名称不能使用中文
- 项目必须使用 Vue 3
- 详见:注意事项(官方文档)
至此,你已经创建好了一个空的 uni-app 项目,现在可以先运行到模拟器上查看效果。
2.2 运行项目
在上述环节中,你已经下载好了一个模拟器,此时可在 DevEco Studio 的【设备管理器】中启动模拟器。
待模拟器启动完成以后,回到 HBuilder X:点击【项目】→【运行】→选择模拟器→【运行】。
win端选择 强制继续运行
等待运行完成,你会看到应用已在模拟器中自动启动。
接下来就是完成代码的编写工作,以下是我的项目架构设计,采用经典的TabBar结构,完美适配鸿蒙设计规范
food-health/
├── components/ # 自定义组件
├── pages/ # 页面文件
├── static/ # 静态资源
│ ├── data/ # 数据文件
│ ├── images/ # 图片资源
│ └── styles/ # 样式文件
│ ├── tailwind.scss
│ └── iconfont.css
├── utils/ # 工具函数
│ ├── formatUtils.js # 格式化工具
│ └── helper.js # 辅助函数
├── uni_modules/ # uni-app 插件模块
├── App.vue # 应用入口
├── main.js # 主入口文件
├── manifest.json # 应用配置
└── pages.json # 页面路由配置
三、上架前的准备
3.1 私钥库文件
1.生成私钥和证书请求文件
- 打开 DevEco Studio,点击【构建】→【生成私钥和证书请求文件】,在弹窗中按照提示填写信息。
- 生成完成后,打开刚才选择的文件夹,你会看到对应的文件与
material文件夹。注意:如需移动,请务必连同
material一起移动。
2. 证书文件
- 华为 AGC 平台,【证书、APP ID 和 Profile】→【证书】选择【新增证书】。
- 根据页面提示填写并完成创建。
- 创建完成后下载,后续发布会用到,建议跟 私钥文件 放在同一个文件夹下。
注意:每个账号最多可申请3个发布证书。一个证书可以多个项目使用。
3. App创建
- 华为 AGC 平台,【证书、APP ID 和 Profile】→【APP ID】选择【新建】。
-
根据页面提示填写并完成创建。
4. Profile创建
- 华为 AGC 平台,【证书、APP ID 和 Profile】→【Profile】选择【添加】。
- 根据页面提示填写并完成创建。
- 创建后下载
注意:每个应用允许申请创建100个Profile文件。Profile使用需对应上选择的应用包名
3.2 鸿蒙相关配置
至此,我们已经准备好了发布相关的证书,已经完成代码的编写工作,接下来可以进行鸿蒙相关的配置,为发布应用做准备。
证书配置
HBuilder X 打开项目 -> manifest.json -> 鸿蒙App配置 -> 填写应用包名(包名这一步也可以在【3.1】3小点后填写 )-> 配置发布证书
按照如图所示选择相关的文件配置,这几个文件在之前的步骤中我们都已经准备好,直接选择填写信息就可以
图标的配置
- 【鸿蒙App配置】-> 【图标配置】
- 需要准备一个或两个 1024 * 1024 的图标用于配置 前景图 背景图 启动界面中部图标
- 启动界面背景色 可以根据需要配置,默认是白色
注意:另外还需要准备一个 216*216的图标用于发布应用时在华为AGC上传
3.3 打包应用
至此,你已经配置好上架前的所有的准备,接下来就是打包
HBuilder X 打开项目 →【发行】→【构建】→【App-Harmony-本地打包】-> [生成安装包]
静静等待打包完成,打包完成后会在控制台输出安装包的路径(不得不夸一下 HBuilder X 一键打包非常舒爽)
3.4 云测试
有些开发者没有鸿蒙真机进行测试,那么这时候我们就可以利用华为的云测试进行上架前的测试,云测试每个用户都拥有一定的免费时长,基本上是够用的。云测试可以确保应用在鸿蒙系统上正常运行,同时也能降低审核时被拒绝的风险。不过这一步在实际运作中发现需要等待的时间比较长,可以根据需要进行执行。
- 华为 AGC 平台,【开发与服务】→【质量】—>【云测试】->【创建测试】——>根据要求填写相关的信息
- 等待测试完成可以查看相关的测试报告,云测试的测试报告非常详细,可以根据测试中不通过的项目进行优化
四 上架应用
至此,已经完成了所有的准备工作,接下来就是审核上架应用了【撒花】。
- 华为 AGC 平台,【证书、APP ID 和 Profile】→【APPID】→【发布】。
- 【应用信息】中填写相关的应用信息,刚才准备的216*216的图标在这一步中上传。
- 【软件包管理】上传刚才打包好的安装包。
- 【版本信息】填写相关的版本信息,其中这一步的隐私政策可以使用华为托管。
- 【应用介绍截图】可以使用模拟器进行截图
注意: 这一步填写完每一个信息请随手点击保存,我就因为没有点击保存也没注意提示,跳转到其他界面后回来还要重新填写
至此,到这一步,就可以点击提交审核进行APP审核通过后自动上架。祝开发者们都能多多开发优质应用,也希望这篇文章对你有用。文末附上我用到通用公共css文件
零、 前言
用一个实战出发,结合自身从开发到上架的全过程经验,尽可能细致地描述每一个“关键步骤”。不管是刚接触鸿蒙开发的新手,还是已有经验的开发者,都希望能从本篇文章中受益。
-
需求制定
要开发一个应用,首先要明白自己想要做什么。这包括确定应用的功能、用户需求等。只有明确了需求,开发应用才能有方向。可以参考多款竞品,也可以发给AI分析应用功能,如:我想要开发一款专注于健康饮食的应用,提供丰富的营养成分查询、多种健康计算工具、个性化食谱推荐等功能,帮助用户科学管理饮食健康。这时候就可以把需求发给AI帮忙分析落地功能 -
技术选型
uni-app 对鸿蒙的良好支持,选择使用 uni-app + Vue 3 作为开发框架。同时利用uni-ui官方组件库,快速搭建应用界面。一、开发前的准备
在这一章节中,将讲述使用 uni-app 开发鸿蒙 App 的前置工作,供开发者参考。
1.1 开发环境的搭建
软件环境
- 操作系统:Windows 10
- 浏览器:版本 142.0.7444.60(正式版,64 位)
说明:虽然是开发鸿蒙 App,但在一些样式调试或隐性数据操作中,先使用浏览器能更快定位问题。在 uni-app 的加持下,浏览器中的表现与鸿蒙上基本一致(除部分鸿蒙特性外)。
-
HBuilder X:4.76(截至发稿我已升级至 4.84,适配了很多鸿蒙相关更新,未更新的同学请及时更新)
-
DevEco Studio:5.1.1 Beta(实战项目环境)/ 6.0.0 Release(新项目推荐)
兼容性参考:官方文档(点此前往)
下载地址:DevEco Studio 下载(点此前往)
1.2 技术栈
- 前端框架:uni-app + Vue 3(Composition API,`<script setup>`)
- UI 组件:uni-ui
- 数据存储:Pinia([参考文档](https://uniapp.dcloud.net.cn/tutorial/vue3-pinia.html#%E7%8A%B6%E6%80%81%E7%AE%A1%E7%90%86-pinia))
1.3 鸿蒙环境及模拟器
- 打开 DevEco Studio 并创建空白项目
- 点击【工具】→【设备管理器】选择一个
API 19及以上的设备下载并安装
二、项目创建
2.1 创建项目
- 打开 HBuilder X:点击左上角【文件】→【新建】→【uni-app】→【uni-app 项目】
- 填写项目名称(建议英文或数字,不要使用中文)
- 选择框架:Vue 3(默认)
- 点击【完成】,创建项目
注意:
- 项目名称不能使用中文
- 项目必须使用 Vue 3
- 详见:注意事项(官方文档)
至此,你已经创建好了一个空的 uni-app 项目,现在可以先运行到模拟器上查看效果。
2.2 运行项目
在上述环节中,你已经下载好了一个模拟器,此时可在 DevEco Studio 的【设备管理器】中启动模拟器。
待模拟器启动完成以后,回到 HBuilder X:点击【项目】→【运行】→选择模拟器→【运行】。
win端选择 强制继续运行
等待运行完成,你会看到应用已在模拟器中自动启动。
接下来就是完成代码的编写工作,以下是我的项目架构设计,采用经典的TabBar结构,完美适配鸿蒙设计规范
food-health/
├── components/ # 自定义组件
├── pages/ # 页面文件
├── static/ # 静态资源
│ ├── data/ # 数据文件
│ ├── images/ # 图片资源
│ └── styles/ # 样式文件
│ ├── tailwind.scss
│ └── iconfont.css
├── utils/ # 工具函数
│ ├── formatUtils.js # 格式化工具
│ └── helper.js # 辅助函数
├── uni_modules/ # uni-app 插件模块
├── App.vue # 应用入口
├── main.js # 主入口文件
├── manifest.json # 应用配置
└── pages.json # 页面路由配置
三、上架前的准备
3.1 私钥库文件
1.生成私钥和证书请求文件
- 打开 DevEco Studio,点击【构建】→【生成私钥和证书请求文件】,在弹窗中按照提示填写信息。
- 生成完成后,打开刚才选择的文件夹,你会看到对应的文件与
material文件夹。注意:如需移动,请务必连同
material一起移动。
2. 证书文件
- 华为 AGC 平台,【证书、APP ID 和 Profile】→【证书】选择【新增证书】。
- 根据页面提示填写并完成创建。
- 创建完成后下载,后续发布会用到,建议跟 私钥文件 放在同一个文件夹下。
注意:每个账号最多可申请3个发布证书。一个证书可以多个项目使用。
3. App创建
- 华为 AGC 平台,【证书、APP ID 和 Profile】→【APP ID】选择【新建】。
-
根据页面提示填写并完成创建。
4. Profile创建
- 华为 AGC 平台,【证书、APP ID 和 Profile】→【Profile】选择【添加】。
- 根据页面提示填写并完成创建。
- 创建后下载
注意:每个应用允许申请创建100个Profile文件。Profile使用需对应上选择的应用包名
3.2 鸿蒙相关配置
至此,我们已经准备好了发布相关的证书,已经完成代码的编写工作,接下来可以进行鸿蒙相关的配置,为发布应用做准备。
证书配置
HBuilder X 打开项目 -> manifest.json -> 鸿蒙App配置 -> 填写应用包名(包名这一步也可以在【3.1】3小点后填写 )-> 配置发布证书
按照如图所示选择相关的文件配置,这几个文件在之前的步骤中我们都已经准备好,直接选择填写信息就可以
图标的配置
- 【鸿蒙App配置】-> 【图标配置】
- 需要准备一个或两个 1024 * 1024 的图标用于配置 前景图 背景图 启动界面中部图标
- 启动界面背景色 可以根据需要配置,默认是白色
注意:另外还需要准备一个 216*216的图标用于发布应用时在华为AGC上传
3.3 打包应用
至此,你已经配置好上架前的所有的准备,接下来就是打包
HBuilder X 打开项目 →【发行】→【构建】→【App-Harmony-本地打包】-> [生成安装包]
静静等待打包完成,打包完成后会在控制台输出安装包的路径(不得不夸一下 HBuilder X 一键打包非常舒爽)
3.4 云测试
有些开发者没有鸿蒙真机进行测试,那么这时候我们就可以利用华为的云测试进行上架前的测试,云测试每个用户都拥有一定的免费时长,基本上是够用的。云测试可以确保应用在鸿蒙系统上正常运行,同时也能降低审核时被拒绝的风险。不过这一步在实际运作中发现需要等待的时间比较长,可以根据需要进行执行。
- 华为 AGC 平台,【开发与服务】→【质量】—>【云测试】->【创建测试】——>根据要求填写相关的信息
- 等待测试完成可以查看相关的测试报告,云测试的测试报告非常详细,可以根据测试中不通过的项目进行优化
四 上架应用
至此,已经完成了所有的准备工作,接下来就是审核上架应用了【撒花】。
- 华为 AGC 平台,【证书、APP ID 和 Profile】→【APPID】→【发布】。
- 【应用信息】中填写相关的应用信息,刚才准备的216*216的图标在这一步中上传。
- 【软件包管理】上传刚才打包好的安装包。
- 【版本信息】填写相关的版本信息,其中这一步的隐私政策可以使用华为托管。
- 【应用介绍截图】可以使用模拟器进行截图
注意: 这一步填写完每一个信息请随手点击保存,我就因为没有点击保存也没注意提示,跳转到其他界面后回来还要重新填写
至此,到这一步,就可以点击提交审核进行APP审核通过后自动上架。祝开发者们都能多多开发优质应用,也希望这篇文章对你有用。文末附上我用到通用公共css文件
收起阅读 »从Web到鸿蒙:uni-app x 开发踩坑与成长实录
一、起因
去年年底,我决定做一个冰箱食材管理的 App,主要功能是记录食材、推荐菜谱,顺便接入 AI 生成个性化建议。
技术选型的时候,我面临一个选择:是分别写 iOS、Android 和鸿蒙三套代码,还是用跨平台框架?作为一个前端开发者,我自然选择了后者,最终决定用 uni-app x。
选择 uni-app x 的原因很简单:官方说支持鸿蒙,而且可以用类似 TypeScript 的语法写代码。我当时想,这不就是 Web 开发的思路吗?写一套代码,三个平台都能跑,多省事。
结果没想到,光是适配鸿蒙就踩了无数的坑。项目做到现在,我最深的体会是:跨平台开发不是"一次编写,处处运行",而是"一次编写,处处适配"。
这篇文章记录了我在开发过程中遇到的几个典型问题,希望能帮到后来者。
二、UTS 不是 TypeScript
uni-app x 使用 UTS 语言,官方文档说它"类似 TypeScript"。我一开始以为只是换了个名字,代码照写就行了。
结果第一天就被打脸了。
1. Map.forEach 不支持
我在统计功能里用了 Map 来记录食材出现次数,写了这样的代码:
const ingredientMap = new Map<string, number>()
ingredientMap.set('鸡蛋', 3)
ingredientMap.set('番茄', 2)
const stats: IngredientStat[] = []
ingredientMap.forEach((count: number, name: string) => {
stats.push({ name, count })
})
在 iOS 上运行正常,一到鸿蒙就报错:undefined is not callable。
调试了半天才发现,鸿蒙平台的 Map 实现不支持 forEach 方法。这让我很惊讶,因为 forEach 在 JavaScript 里是最基础的 API 之一。
解决办法是改用 for...of 循环:
const stats: IngredientStat[] = []
for (const [name, count] of ingredientMap.entries()) {
stats.push({ name: name, count: count } as IngredientStat)
}
这个改动不难,但问题是我没想到会有这样的限制。这让我意识到,UTS 虽然语法像 TypeScript,但底层编译到 Kotlin/Swift 时,会受到原生平台的约束。
2. 类型隐式转换被禁止
还有一个问题,是条件判断。我习惯这样写:
if (data) {
// 处理数据
}
这在 Web 开发里很常见,但在 UTS 里会报错。因为 UTS 是强类型语言,条件表达式必须是布尔类型,不能用"真值/假值"的概念。
正确的写法是:
if (data != null) {
// 处理数据
}
这种严格的类型检查一开始让我很不适应,但后来发现它确实能避免很多隐藏的 Bug。毕竟 JavaScript 的类型转换规则太复杂了,0、''、null、undefined 在条件判断里的行为都不一样。
3. onLoad 参数的陷阱
页面加载时,我需要从 URL 参数里获取数据。我看官方文档说 onLoad 的参数是 Map 类型,就写了这样的代码:
onLoad((options: Map<string, any | null>) => {
const id = options.get('id')
})
结果又报错了:undefined is not callable。
后来才知道,虽然文档说是 Map 类型,但在鸿蒙平台上,options 实际上是个普通对象。正确的写法是:
onLoad((options: any) => {
if (options !== null && options.id !== null) {
const id = options.id as string
}
})
这个问题让我明白,文档和实际实现之间可能会有差异,不能完全依赖类型标注,要以实际运行结果为准。
三、最难查的 Bug:数据库查询的平台差异
说起来,前面这些坑虽然麻烦,但至少报错信息还算明确。最让我头疼的是一个"看不见的 Bug"。
统计页面在 iOS 上显示正常,数据都对。但一到鸿蒙上,所有统计数字全是 0。界面正常,没有报错,就是数据不对。
问题排查
我先检查了数据库操作,发现查询 SQL 没问题,也能正常执行。然后加了一堆 console.log,发现查询结果确实返回了:
const result = db.query('SELECT COUNT(*) as count FROM saved_recipes')
console.log('查询结果:', JSON.stringify(result.maps))
// iOS: [{"count": 5}]
// 鸿蒙: [{}] // 看起来像空对象?
但是取值的时候就出问题了:
const count = result.maps[0]['count']
console.log('统计数量:', count)
// iOS: 5
// 鸿蒙: undefined
我当时就懵了,明明查询结果里有数据,为什么取不到?
问题根源
查了半天资料,最后在一个 issue 里看到有人提到:鸿蒙平台的查询结果不是普通对象数组,而是 Map<string, any>[]。
这就解释了为什么 JSON.stringify 输出是空对象——Map 对象序列化后本来就是空的。而在 iOS/Android 上,查询结果是普通对象,所以 item['count'] 可以正常取值,但在鸿蒙上必须用 item.get('count')。
这个差异太隐蔽了,因为:
- 没有报错信息
result.maps的长度是正确的- 直接打印对象看不出问题
解决方案
我在基础 Service 类里封装了一个通用方法:
protected static getFieldValue(item: any, field: string, defaultValue: any = null): any {
if (item instanceof Map) {
// 鸿蒙平台:Map 对象
return (item as Map<string, any>).get(field) ?? defaultValue
} else {
// 其他平台:普通对象
return item[field] ?? defaultValue
}
}
然后在所有查询结果处理的地方都改用这个方法:
const result = db.query('SELECT COUNT(*) as count FROM saved_recipes')
const row = result.maps[0]
const count = this.getFieldValue(row, 'count', 0) as number
这样就兼容所有平台了。
深层思考
这个 Bug 让我对跨平台开发有了新的认识。
在 Web 开发里,JavaScript 的数据结构在各个浏览器上基本是一致的。但在跨平台开发里,即使是数组、对象这样的基础数据结构,在不同平台上的实现也可能不同。
鸿蒙之所以返回 Map 对象,可能是因为 UTS 编译到 Kotlin 后,用了 Kotlin 的 Map 类型。而 iOS/Android 可能用的是字典或者其他数据结构,反射到 UTS 层面就是普通对象。
这种差异不是框架的 Bug,而是跨平台开发的本质特征。框架只能保证 API 层面的一致性,底层数据结构的差异还是要开发者自己处理。
四、页面滚动的迷惑行为
还有一个坑,让我调试了整整一个下午。
AI 生成菜谱的结果页面,内容很长,需要滚动查看。我一开始用了 Web 开发的常规思路:
<template>
<view class="page">
<x-navbar title="菜谱详情" />
<scroll-view class="content" scroll-y>
<!-- 菜谱内容 -->
</scroll-view>
</view>
</template>
<style>
.page {
height: 100vh;
}
.content {
flex: 1;
}
</style>
结果页面完全不滚动。
连环踩坑
我开始排查问题:
第一步:去掉 scroll-view,想让页面自然滚动。不行。
第二步:调用 uni.pageScrollTo 滚动到底部。报错:selector invalid。
第三步:检查了半天 CSS,发现鸿蒙不支持 vh 单位。改成 100%,还是不行。
最后我仔细看了一遍页面结构,才发现问题:
<template>
<scroll-view class="content">
<!-- 内容 -->
</scroll-view>
<x-snackbar /> <!-- 提示组件 -->
</template>
我把 Snackbar 组件放在了 scroll-view 外面,导致 <template> 下有两个根节点。这样一来,scroll-view 就不是真正的页面滚动容器了。
正确方案
正确的做法是,scroll-view 必须是 <template> 下的唯一根标签,所有其他组件都要放在里面:
<template>
<scroll-view class="page">
<x-navbar title="菜谱详情" />
<!-- 内容 -->
<x-snackbar /> <!-- 也要放里面 -->
</scroll-view>
</template>
<style>
.page {
width: 100%;
height: 100%; /* 用 100% 而不是 100vh */
padding-bottom: calc(24rpx + env(safe-area-inset-bottom)); /* 安全距离 */
}
</style>
改完之后,滚动立刻正常了,uni.pageScrollTo 也能用了。
为什么这么设计?
我后来想明白了,这是因为 uni-app x 的页面模型和 Web 不同。
在 Web 里,<body> 标签天然就是滚动容器,你写多少内容都会自动滚动。但在原生应用里,页面不会自动滚动,必须明确指定一个滚动容器。
鸿蒙的 CSS 支持也比 Web 少得多,没有 vh/vw 单位,没有 position: sticky,很多 Web 开发的技巧都用不了。这逼着我用原生应用的思路来写布局。
五、收获
踩了这么多坑,我最大的感受是:跨平台开发的难点不在于语法,而在于平台差异。
uni-app x 已经做得很好了,它把三个平台的 API 统一了,让我可以用一套代码来写。但是,底层数据结构、CSS 支持、API 实现细节上的差异,还是需要开发者自己去适配。
给后来者的几点建议:
-
仔细阅读 UTS 与 TypeScript 的差异文档。不要想当然地认为它们是一样的。
-
善用
instanceof和类型判断。在处理查询结果、事件参数等可能有平台差异的地方,先判断类型再操作。 -
详细的日志是最好的调试工具。把关键数据都打印出来,包括类型信息,能快速定位问题。
-
页面布局用原生思维。不要用 Web 那套
vh/vw、position: sticky,老老实实用scroll-view和flex布局。 -
记录踩坑过程。我把每个问题都写成了文档,方便以后查阅,也能分享给团队其他人。
最后想说的是,虽然踩了很多坑,但我并不后悔选择 uni-app x。相比分别写三套原生代码,它已经节省了我大量时间。而且踩坑的过程也让我对跨平台开发有了更深的理解。
从 Web 思维到原生思维的转变,是一个痛苦但必要的过程。这可能就是成长的代价吧。
一、起因
去年年底,我决定做一个冰箱食材管理的 App,主要功能是记录食材、推荐菜谱,顺便接入 AI 生成个性化建议。
技术选型的时候,我面临一个选择:是分别写 iOS、Android 和鸿蒙三套代码,还是用跨平台框架?作为一个前端开发者,我自然选择了后者,最终决定用 uni-app x。
选择 uni-app x 的原因很简单:官方说支持鸿蒙,而且可以用类似 TypeScript 的语法写代码。我当时想,这不就是 Web 开发的思路吗?写一套代码,三个平台都能跑,多省事。
结果没想到,光是适配鸿蒙就踩了无数的坑。项目做到现在,我最深的体会是:跨平台开发不是"一次编写,处处运行",而是"一次编写,处处适配"。
这篇文章记录了我在开发过程中遇到的几个典型问题,希望能帮到后来者。
二、UTS 不是 TypeScript
uni-app x 使用 UTS 语言,官方文档说它"类似 TypeScript"。我一开始以为只是换了个名字,代码照写就行了。
结果第一天就被打脸了。
1. Map.forEach 不支持
我在统计功能里用了 Map 来记录食材出现次数,写了这样的代码:
const ingredientMap = new Map<string, number>()
ingredientMap.set('鸡蛋', 3)
ingredientMap.set('番茄', 2)
const stats: IngredientStat[] = []
ingredientMap.forEach((count: number, name: string) => {
stats.push({ name, count })
})
在 iOS 上运行正常,一到鸿蒙就报错:undefined is not callable。
调试了半天才发现,鸿蒙平台的 Map 实现不支持 forEach 方法。这让我很惊讶,因为 forEach 在 JavaScript 里是最基础的 API 之一。
解决办法是改用 for...of 循环:
const stats: IngredientStat[] = []
for (const [name, count] of ingredientMap.entries()) {
stats.push({ name: name, count: count } as IngredientStat)
}
这个改动不难,但问题是我没想到会有这样的限制。这让我意识到,UTS 虽然语法像 TypeScript,但底层编译到 Kotlin/Swift 时,会受到原生平台的约束。
2. 类型隐式转换被禁止
还有一个问题,是条件判断。我习惯这样写:
if (data) {
// 处理数据
}
这在 Web 开发里很常见,但在 UTS 里会报错。因为 UTS 是强类型语言,条件表达式必须是布尔类型,不能用"真值/假值"的概念。
正确的写法是:
if (data != null) {
// 处理数据
}
这种严格的类型检查一开始让我很不适应,但后来发现它确实能避免很多隐藏的 Bug。毕竟 JavaScript 的类型转换规则太复杂了,0、''、null、undefined 在条件判断里的行为都不一样。
3. onLoad 参数的陷阱
页面加载时,我需要从 URL 参数里获取数据。我看官方文档说 onLoad 的参数是 Map 类型,就写了这样的代码:
onLoad((options: Map<string, any | null>) => {
const id = options.get('id')
})
结果又报错了:undefined is not callable。
后来才知道,虽然文档说是 Map 类型,但在鸿蒙平台上,options 实际上是个普通对象。正确的写法是:
onLoad((options: any) => {
if (options !== null && options.id !== null) {
const id = options.id as string
}
})
这个问题让我明白,文档和实际实现之间可能会有差异,不能完全依赖类型标注,要以实际运行结果为准。
三、最难查的 Bug:数据库查询的平台差异
说起来,前面这些坑虽然麻烦,但至少报错信息还算明确。最让我头疼的是一个"看不见的 Bug"。
统计页面在 iOS 上显示正常,数据都对。但一到鸿蒙上,所有统计数字全是 0。界面正常,没有报错,就是数据不对。
问题排查
我先检查了数据库操作,发现查询 SQL 没问题,也能正常执行。然后加了一堆 console.log,发现查询结果确实返回了:
const result = db.query('SELECT COUNT(*) as count FROM saved_recipes')
console.log('查询结果:', JSON.stringify(result.maps))
// iOS: [{"count": 5}]
// 鸿蒙: [{}] // 看起来像空对象?
但是取值的时候就出问题了:
const count = result.maps[0]['count']
console.log('统计数量:', count)
// iOS: 5
// 鸿蒙: undefined
我当时就懵了,明明查询结果里有数据,为什么取不到?
问题根源
查了半天资料,最后在一个 issue 里看到有人提到:鸿蒙平台的查询结果不是普通对象数组,而是 Map<string, any>[]。
这就解释了为什么 JSON.stringify 输出是空对象——Map 对象序列化后本来就是空的。而在 iOS/Android 上,查询结果是普通对象,所以 item['count'] 可以正常取值,但在鸿蒙上必须用 item.get('count')。
这个差异太隐蔽了,因为:
- 没有报错信息
result.maps的长度是正确的- 直接打印对象看不出问题
解决方案
我在基础 Service 类里封装了一个通用方法:
protected static getFieldValue(item: any, field: string, defaultValue: any = null): any {
if (item instanceof Map) {
// 鸿蒙平台:Map 对象
return (item as Map<string, any>).get(field) ?? defaultValue
} else {
// 其他平台:普通对象
return item[field] ?? defaultValue
}
}
然后在所有查询结果处理的地方都改用这个方法:
const result = db.query('SELECT COUNT(*) as count FROM saved_recipes')
const row = result.maps[0]
const count = this.getFieldValue(row, 'count', 0) as number
这样就兼容所有平台了。
深层思考
这个 Bug 让我对跨平台开发有了新的认识。
在 Web 开发里,JavaScript 的数据结构在各个浏览器上基本是一致的。但在跨平台开发里,即使是数组、对象这样的基础数据结构,在不同平台上的实现也可能不同。
鸿蒙之所以返回 Map 对象,可能是因为 UTS 编译到 Kotlin 后,用了 Kotlin 的 Map 类型。而 iOS/Android 可能用的是字典或者其他数据结构,反射到 UTS 层面就是普通对象。
这种差异不是框架的 Bug,而是跨平台开发的本质特征。框架只能保证 API 层面的一致性,底层数据结构的差异还是要开发者自己处理。
四、页面滚动的迷惑行为
还有一个坑,让我调试了整整一个下午。
AI 生成菜谱的结果页面,内容很长,需要滚动查看。我一开始用了 Web 开发的常规思路:
<template>
<view class="page">
<x-navbar title="菜谱详情" />
<scroll-view class="content" scroll-y>
<!-- 菜谱内容 -->
</scroll-view>
</view>
</template>
<style>
.page {
height: 100vh;
}
.content {
flex: 1;
}
</style>
结果页面完全不滚动。
连环踩坑
我开始排查问题:
第一步:去掉 scroll-view,想让页面自然滚动。不行。
第二步:调用 uni.pageScrollTo 滚动到底部。报错:selector invalid。
第三步:检查了半天 CSS,发现鸿蒙不支持 vh 单位。改成 100%,还是不行。
最后我仔细看了一遍页面结构,才发现问题:
<template>
<scroll-view class="content">
<!-- 内容 -->
</scroll-view>
<x-snackbar /> <!-- 提示组件 -->
</template>
我把 Snackbar 组件放在了 scroll-view 外面,导致 <template> 下有两个根节点。这样一来,scroll-view 就不是真正的页面滚动容器了。
正确方案
正确的做法是,scroll-view 必须是 <template> 下的唯一根标签,所有其他组件都要放在里面:
<template>
<scroll-view class="page">
<x-navbar title="菜谱详情" />
<!-- 内容 -->
<x-snackbar /> <!-- 也要放里面 -->
</scroll-view>
</template>
<style>
.page {
width: 100%;
height: 100%; /* 用 100% 而不是 100vh */
padding-bottom: calc(24rpx + env(safe-area-inset-bottom)); /* 安全距离 */
}
</style>
改完之后,滚动立刻正常了,uni.pageScrollTo 也能用了。
为什么这么设计?
我后来想明白了,这是因为 uni-app x 的页面模型和 Web 不同。
在 Web 里,<body> 标签天然就是滚动容器,你写多少内容都会自动滚动。但在原生应用里,页面不会自动滚动,必须明确指定一个滚动容器。
鸿蒙的 CSS 支持也比 Web 少得多,没有 vh/vw 单位,没有 position: sticky,很多 Web 开发的技巧都用不了。这逼着我用原生应用的思路来写布局。
五、收获
踩了这么多坑,我最大的感受是:跨平台开发的难点不在于语法,而在于平台差异。
uni-app x 已经做得很好了,它把三个平台的 API 统一了,让我可以用一套代码来写。但是,底层数据结构、CSS 支持、API 实现细节上的差异,还是需要开发者自己去适配。
给后来者的几点建议:
-
仔细阅读 UTS 与 TypeScript 的差异文档。不要想当然地认为它们是一样的。
-
善用
instanceof和类型判断。在处理查询结果、事件参数等可能有平台差异的地方,先判断类型再操作。 -
详细的日志是最好的调试工具。把关键数据都打印出来,包括类型信息,能快速定位问题。
-
页面布局用原生思维。不要用 Web 那套
vh/vw、position: sticky,老老实实用scroll-view和flex布局。 -
记录踩坑过程。我把每个问题都写成了文档,方便以后查阅,也能分享给团队其他人。
最后想说的是,虽然踩了很多坑,但我并不后悔选择 uni-app x。相比分别写三套原生代码,它已经节省了我大量时间。而且踩坑的过程也让我对跨平台开发有了更深的理解。
从 Web 思维到原生思维的转变,是一个痛苦但必要的过程。这可能就是成长的代价吧。
收起阅读 »
解决小程序分包引用分包下的json文件编译后生成到主包的问题
问题
目前,小程序端,分包引用分包下的json文件编译后会生成到主包中
解决方案
方案一
把 json 文件改成 js 文件,通过 export default 导出,manifest.json 中指定的小程序节点(比如mp-weixin)需要配置
"optimization": {
"subPackages": true
}
方案二
使用 @uni_toolkit/unplugin-json-optimization 插件
安装
# npm
npm install @uni_toolkit/unplugin-json-optimization -D
# yarn
yarn add @uni_toolkit/unplugin-json-optimization -D
# pnpm
pnpm add @uni_toolkit/unplugin-json-optimization -D
使用方法
// vite.config.js
import { defineConfig } from 'vite'
import uni from "@dcloudio/vite-plugin-uni"
import jsonOptimization from '@uni_toolkit/unplugin-json-optimization/vite'
export default defineConfig({
plugins: [
uni(),
jsonOptimization(),
],
}) 问题
目前,小程序端,分包引用分包下的json文件编译后会生成到主包中
解决方案
方案一
把 json 文件改成 js 文件,通过 export default 导出,manifest.json 中指定的小程序节点(比如mp-weixin)需要配置
"optimization": {
"subPackages": true
}
方案二
使用 @uni_toolkit/unplugin-json-optimization 插件
安装
# npm
npm install @uni_toolkit/unplugin-json-optimization -D
# yarn
yarn add @uni_toolkit/unplugin-json-optimization -D
# pnpm
pnpm add @uni_toolkit/unplugin-json-optimization -D
使用方法
// vite.config.js
import { defineConfig } from 'vite'
import uni from "@dcloudio/vite-plugin-uni"
import jsonOptimization from '@uni_toolkit/unplugin-json-optimization/vite'
export default defineConfig({
plugins: [
uni(),
jsonOptimization(),
],
}) 收起阅读 »
【紧急!!】谷歌报READ_MEDIA_IMAGES/READ_MEDIA_VIDEO 权限的相关要求,无法移除这两个权限
之前还好好的,最近发布的app都被拒绝了,提示这两个权限需要移除,由于使用次数和场景不太多,但是我在配置做了移除,谷歌还是扫描出来了,麻烦官方予以介入解决。这块已经直接导致无法上架最新版本的包了。是否有能解决办法,并且能兼容ios和安卓所有场景的使用。
之前还好好的,最近发布的app都被拒绝了,提示这两个权限需要移除,由于使用次数和场景不太多,但是我在配置做了移除,谷歌还是扫描出来了,麻烦官方予以介入解决。这块已经直接导致无法上架最新版本的包了。是否有能解决办法,并且能兼容ios和安卓所有场景的使用。
在 UniApp 中用 UTS 封装钉钉登录(HarmonyOS)
在 UniApp 中用 UTS 封装钉钉登录(HarmonyOS)
插件开发背景
业务需要在鸿蒙端提供一键授权登录的统一能力。钉钉开放平台已提供标准 ArkTS 能力,通过 ArkUI 组件与 UTS 的嵌入式原生组件机制,将授权登录流程抽象为一个可复用的页面 <embed> 组件。目标是:最少的页面接入代码、清晰的事件回调、可控的依赖与权限声明。仅考虑 ArkTS API。
这里用到了嵌入原生组件能力。
鸿蒙原生实现(ArkUI + OpenAuth)
核心在 ETS 侧完成交互和授权触发,依赖 @dingtalk/openauth:
// utssdk/app-harmony/button.ets(示意结构)
import { NativeEmbedBuilderOptions, defineNativeEmbed } from "@dcloudio/uni-app-runtime"
import { DDAuthApiFactory, AuthLoginParam } from '@dingtalk/openauth'
interface ButtonBuilderOptions extends NativeEmbedBuilderOptions {
label: string
// 其他自定义参数,如登录场景、回调地址等
}
@Component
struct ButtonComponent {
@Prop label: string
onButtonClick?: Function
build() {
Button(this.label)
.width('100%')
.height('100%')
.onClick(() => {
if (this.onButtonClick) {
const param: AuthLoginParam = {
// 根据业务填充必要参数,例如 appId、scope、state 等
}
DDAuthApiFactory.createDDAuthApi(param).authLogin(this)
this.onButtonClick({ detail: { status: 'pending' } })
}
})
}
}
@Builder
function ButtonBuilder(options: ButtonBuilderOptions) {
ButtonComponent({
label: options.label,
onButtonClick: options?.on?.get('buttonclick')
})
.width(options.width)
.height(options.height)
}
defineNativeEmbed('button', { builder: ButtonBuilder })
要点说明:
Button负责视觉与交互,授权调用走DDAuthApiFactory。- *通过
options.on映射到组件内部回调,事件名约定为全小写(如buttonclick)。 - UTS/ETS 不支持结构化类型等价,事件
detail与自定义参数类型要统一定义和复用。
UTS 封装实现(目录、依赖、导出)
目录结构:
uni_modules/harmony-dingding-login/
└─ utssdk/
├─ app-harmony/
│ ├─ index.uts
│ ├─ button.ets
│ ├─ config.json
│ └─ module.json5
└─ interface.uts
关键文件:
utssdk/app-harmony/config.json引入三方依赖(已内置):
{
"dependencies": {
"@dingtalk/openauth": "^1.1.0"
}
}
utssdk/app-harmony/index.uts负责激活原生组件:
// 内容即导入 ETS,触发注册
import './button.ets'
utssdk/interface.uts建议集中声明:事件细节、错误码、可选参数类型,确保 UTS/ETS 双侧一致。
用户使用说明(页面接入)
最少 3 步:
- 在页面引入插件:
import '@/uni_modules/harmony-dingding-login' - 在模板中使用
<embed>:指定tag="button"、绑定@buttonclick - 组织
options:包含按钮文案、宽高、以及钉钉授权所需参数
示例:
<template>
<view>
<embed class="dd-login" tag="button" :options="options" @buttonclick="onLogin" />
</view>
<!-- 建议宽高固定,避免布局跳动 -->
</template>
<script>
import '@/uni_modules/harmony-dingding-login'
export default {
data() {
return {
options: {
width: 200,
height: 44,
label: '钉钉登录',
on: new Map(), // 可按需传入,也可使用 @buttonclick 监听
// 可放置与授权相关的业务参数,以便在 ETS 读取
// appId、scope、state 等
}
}
},
methods: {
onLogin(e) {
// e.detail 建议包含授权阶段/结果数据
console.log('dingding auth:', e.detail)
// 与后端交换 code/token,完成会话建立
}
}
}
</script>
<style scoped>
.dd-login { display: block; width: 200px; height: 44px; }
</style>
注意事项
- 依赖已内置:
@dingtalk/openauth版本^1.1.0写入utssdk/app-harmony/config.json。
有疑问可留言说明
遇到授权参数、事件结构、标签命名、真机兼容等问题,直接留言描述运行环境与报错信息(含系统版本、设备型号、必要截图)。
在 UniApp 中用 UTS 封装钉钉登录(HarmonyOS)
插件开发背景
业务需要在鸿蒙端提供一键授权登录的统一能力。钉钉开放平台已提供标准 ArkTS 能力,通过 ArkUI 组件与 UTS 的嵌入式原生组件机制,将授权登录流程抽象为一个可复用的页面 <embed> 组件。目标是:最少的页面接入代码、清晰的事件回调、可控的依赖与权限声明。仅考虑 ArkTS API。
这里用到了嵌入原生组件能力。
鸿蒙原生实现(ArkUI + OpenAuth)
核心在 ETS 侧完成交互和授权触发,依赖 @dingtalk/openauth:
// utssdk/app-harmony/button.ets(示意结构)
import { NativeEmbedBuilderOptions, defineNativeEmbed } from "@dcloudio/uni-app-runtime"
import { DDAuthApiFactory, AuthLoginParam } from '@dingtalk/openauth'
interface ButtonBuilderOptions extends NativeEmbedBuilderOptions {
label: string
// 其他自定义参数,如登录场景、回调地址等
}
@Component
struct ButtonComponent {
@Prop label: string
onButtonClick?: Function
build() {
Button(this.label)
.width('100%')
.height('100%')
.onClick(() => {
if (this.onButtonClick) {
const param: AuthLoginParam = {
// 根据业务填充必要参数,例如 appId、scope、state 等
}
DDAuthApiFactory.createDDAuthApi(param).authLogin(this)
this.onButtonClick({ detail: { status: 'pending' } })
}
})
}
}
@Builder
function ButtonBuilder(options: ButtonBuilderOptions) {
ButtonComponent({
label: options.label,
onButtonClick: options?.on?.get('buttonclick')
})
.width(options.width)
.height(options.height)
}
defineNativeEmbed('button', { builder: ButtonBuilder })
要点说明:
Button负责视觉与交互,授权调用走DDAuthApiFactory。- *通过
options.on映射到组件内部回调,事件名约定为全小写(如buttonclick)。 - UTS/ETS 不支持结构化类型等价,事件
detail与自定义参数类型要统一定义和复用。
UTS 封装实现(目录、依赖、导出)
目录结构:
uni_modules/harmony-dingding-login/
└─ utssdk/
├─ app-harmony/
│ ├─ index.uts
│ ├─ button.ets
│ ├─ config.json
│ └─ module.json5
└─ interface.uts
关键文件:
utssdk/app-harmony/config.json引入三方依赖(已内置):
{
"dependencies": {
"@dingtalk/openauth": "^1.1.0"
}
}
utssdk/app-harmony/index.uts负责激活原生组件:
// 内容即导入 ETS,触发注册
import './button.ets'
utssdk/interface.uts建议集中声明:事件细节、错误码、可选参数类型,确保 UTS/ETS 双侧一致。
用户使用说明(页面接入)
最少 3 步:
- 在页面引入插件:
import '@/uni_modules/harmony-dingding-login' - 在模板中使用
<embed>:指定tag="button"、绑定@buttonclick - 组织
options:包含按钮文案、宽高、以及钉钉授权所需参数
示例:
<template>
<view>
<embed class="dd-login" tag="button" :options="options" @buttonclick="onLogin" />
</view>
<!-- 建议宽高固定,避免布局跳动 -->
</template>
<script>
import '@/uni_modules/harmony-dingding-login'
export default {
data() {
return {
options: {
width: 200,
height: 44,
label: '钉钉登录',
on: new Map(), // 可按需传入,也可使用 @buttonclick 监听
// 可放置与授权相关的业务参数,以便在 ETS 读取
// appId、scope、state 等
}
}
},
methods: {
onLogin(e) {
// e.detail 建议包含授权阶段/结果数据
console.log('dingding auth:', e.detail)
// 与后端交换 code/token,完成会话建立
}
}
}
</script>
<style scoped>
.dd-login { display: block; width: 200px; height: 44px; }
</style>
注意事项
- 依赖已内置:
@dingtalk/openauth版本^1.1.0写入utssdk/app-harmony/config.json。
有疑问可留言说明
遇到授权参数、事件结构、标签命名、真机兼容等问题,直接留言描述运行环境与报错信息(含系统版本、设备型号、必要截图)。
收起阅读 »【鸿蒙征文】uni-app 现有 UI 库兼容鸿蒙系统开发指南
uni-app 现有 UI 库兼容鸿蒙系统开发指南
核心结论:现有基于 Vue3 开发的 uni-app UI 库插件适配鸿蒙系统的门槛较低,无需大规模重构,重点攻克版本适配、API 兼容性等关键细节后即可正常编译运行。
一、前置核心要求
以下为 UI 库兼容鸿蒙的基础前提,未满足前会导致编译流程直接失败,需优先处理。
1.1 强制升级 Vue 版本至 Vue3
鸿蒙平台对 uni-app 的编译支持仅适配 Vue3 框架,Vue2 版本无法完成编译流程。若当前 UI 库仍基于 Vue2 开发,必须先完成版本升级,具体操作可参考官方权威文档:
官方升级指南:Vue2升级到Vue3指南
1.2 代码风格兼容说明
Vue3 支持选项式 API 和组合式 API(setup 语法糖)两种编码风格,鸿蒙编译环境对两者均完全兼容,无需强制将选项式风格重构为组合式。
提示:如果采用选项式 API 开发可实现「一套代码兼容 Vue2 和 Vue3 双环境」,降低多平台维护成本;
二、关键兼容性处理
完成前置要求后,需针对性处理鸿蒙不支持的核心 API 及对象,这是适配工作的核心环节。
2.1 彻底移除或替代 plus 对象
鸿蒙系统不支持 uni-app 中的 plus 对象(HTML5+ 扩展能力),若 UI 库中存在 plus 对象调用,需根据业务场景选择以下两种方案处理,确保编译通过。
方案 1:功能降级处理(快速适配首选)
这是应急方案,适用于非核心功能依赖 plus 对象的场景,通过条件编译在鸿蒙环境中屏蔽相关功能,保留其他平台的完整性。
核心原理: 利用 uni-app 的条件编译语法,针对鸿蒙平台(标识为 harmony)单独剔除 plus 相关代码块,避免编译报错。
示例代码:
// #ifndef APP-HARMONY
// 鸿蒙不支持的 plus 功能代码
plus.xxx();
// #endif
方案 2:UTS 重构实现(保留完整功能)
这是最完美的方案,通过 UTS 重构原 plus 功能,调用鸿蒙原生 API 实现等效能力。UTS 是 uni-app 跨平台的底层开发语言,可直接对接各平台原生能力。
参考资源:
-
UTS 语法基础:UTS 官方文档
-
鸿蒙原生 API 对接示例:UTS 调用鸿蒙 API 教程
2.2 处理不兼容的 uni.xxx API
目前 uni-app 已实现 90% 以上核心 API 对鸿蒙的支持,但仍有部分 API 存在兼容性限制,需按以下流程处理:
步骤 1:查询 API 兼容性
通过 uni-app 官方文档的「兼容性说明」模块,精准判断 API 是否支持鸿蒙:
- 打开 uni-app API 文档中心;
- 找到目标 API 页面,查看「平台兼容性」表格;
- 若「HarmonyOS」列标注具体版本号(如 3.0+),则支持;若显示「不支持」或空白,则需处理。
步骤 2:替换不支持的 API
与处理plus对象一样,使用【降级处理】或【UTS 重构】方式解决不支持的 API
三、常见问题解决方案
3.1 降级处理详细说明
定义:降级处理是指在鸿蒙平台中,用功能更基础但兼容性更广的实现替代不支持的 API/功能,或在不影响核心体验的前提下屏蔽非必要功能,确保整体流程通顺。
核心原则:「保核心、砍次要」,优先保障 UI 库的渲染、交互等核心能力,对个性化扩展功能可适当简化。
3.2 UTS 重构实操示例
- 鸿蒙原生 API 对接示例:UTS调用鸿蒙API教程
3.3 样式异常处理
目前鸿蒙基本上兼容所有css样式,但如果在鸿蒙上css样式显示异常,可以考虑使用条件编译专门为鸿蒙编写样式。
四、测试与发行流程
适配完成后,需通过官方工具完成编译测试和发行,确保 UI 库在鸿蒙设备上正常运行。
请查看鸿蒙运行和发行
uni-app 现有 UI 库兼容鸿蒙系统开发指南
核心结论:现有基于 Vue3 开发的 uni-app UI 库插件适配鸿蒙系统的门槛较低,无需大规模重构,重点攻克版本适配、API 兼容性等关键细节后即可正常编译运行。
一、前置核心要求
以下为 UI 库兼容鸿蒙的基础前提,未满足前会导致编译流程直接失败,需优先处理。
1.1 强制升级 Vue 版本至 Vue3
鸿蒙平台对 uni-app 的编译支持仅适配 Vue3 框架,Vue2 版本无法完成编译流程。若当前 UI 库仍基于 Vue2 开发,必须先完成版本升级,具体操作可参考官方权威文档:
官方升级指南:Vue2升级到Vue3指南
1.2 代码风格兼容说明
Vue3 支持选项式 API 和组合式 API(setup 语法糖)两种编码风格,鸿蒙编译环境对两者均完全兼容,无需强制将选项式风格重构为组合式。
提示:如果采用选项式 API 开发可实现「一套代码兼容 Vue2 和 Vue3 双环境」,降低多平台维护成本;
二、关键兼容性处理
完成前置要求后,需针对性处理鸿蒙不支持的核心 API 及对象,这是适配工作的核心环节。
2.1 彻底移除或替代 plus 对象
鸿蒙系统不支持 uni-app 中的 plus 对象(HTML5+ 扩展能力),若 UI 库中存在 plus 对象调用,需根据业务场景选择以下两种方案处理,确保编译通过。
方案 1:功能降级处理(快速适配首选)
这是应急方案,适用于非核心功能依赖 plus 对象的场景,通过条件编译在鸿蒙环境中屏蔽相关功能,保留其他平台的完整性。
核心原理: 利用 uni-app 的条件编译语法,针对鸿蒙平台(标识为 harmony)单独剔除 plus 相关代码块,避免编译报错。
示例代码:
// #ifndef APP-HARMONY
// 鸿蒙不支持的 plus 功能代码
plus.xxx();
// #endif
方案 2:UTS 重构实现(保留完整功能)
这是最完美的方案,通过 UTS 重构原 plus 功能,调用鸿蒙原生 API 实现等效能力。UTS 是 uni-app 跨平台的底层开发语言,可直接对接各平台原生能力。
参考资源:
-
UTS 语法基础:UTS 官方文档
-
鸿蒙原生 API 对接示例:UTS 调用鸿蒙 API 教程
2.2 处理不兼容的 uni.xxx API
目前 uni-app 已实现 90% 以上核心 API 对鸿蒙的支持,但仍有部分 API 存在兼容性限制,需按以下流程处理:
步骤 1:查询 API 兼容性
通过 uni-app 官方文档的「兼容性说明」模块,精准判断 API 是否支持鸿蒙:
- 打开 uni-app API 文档中心;
- 找到目标 API 页面,查看「平台兼容性」表格;
- 若「HarmonyOS」列标注具体版本号(如 3.0+),则支持;若显示「不支持」或空白,则需处理。
步骤 2:替换不支持的 API
与处理plus对象一样,使用【降级处理】或【UTS 重构】方式解决不支持的 API
三、常见问题解决方案
3.1 降级处理详细说明
定义:降级处理是指在鸿蒙平台中,用功能更基础但兼容性更广的实现替代不支持的 API/功能,或在不影响核心体验的前提下屏蔽非必要功能,确保整体流程通顺。
核心原则:「保核心、砍次要」,优先保障 UI 库的渲染、交互等核心能力,对个性化扩展功能可适当简化。
3.2 UTS 重构实操示例
- 鸿蒙原生 API 对接示例:UTS调用鸿蒙API教程
3.3 样式异常处理
目前鸿蒙基本上兼容所有css样式,但如果在鸿蒙上css样式显示异常,可以考虑使用条件编译专门为鸿蒙编写样式。
四、测试与发行流程
适配完成后,需通过官方工具完成编译测试和发行,确保 UI 库在鸿蒙设备上正常运行。
请查看鸿蒙运行和发行
收起阅读 »







































