uni-app鸿蒙UTS插件开发实战:调用原生API全流程
在uni-app跨平台开发中,当需要调用鸿蒙系统特有的原生能力(如读取设备信息、打开应用市场详情页)时,UTS插件是关键桥梁。UTS(uni-app TypeScript)允许你在uni-app项目中用TypeScript直接调用鸿蒙ArkTS API,省去了JS桥接开销,性能更优。本文以跳转华为应用市场详情页为例,完整演示从创建到调用的技术细节,并总结踩坑经验。一、新建uni_modules插件
在项目根目录的 'uni_modules' 文件夹(如不存在则新建)内,右键选择“新建uni_modules插件”。插件命名建议用英文,例如 'openAppProduct';注意避免以 'uni-' 开头(官方插件保留前缀),个人插件推荐用开发者标识开头,如 'wq-openAppProduct'。
二、配置package.json声明ArkTS支持
编辑插件根目录的 package.json,在 'uni_modules' 节点下新增 'uni-ext-api' 配置,声明该插件支持鸿蒙ArkTS平台(arkts: true),其他平台对应设为false:
{
"uni_modules": {
"uni-ext-api": {
"uni": {
"openAppProduct": {
"name": "openAppProduct",
"app": {
"js": false,
"kotlin": false,
"swift": false,
"arkts": true
}
}
}
}
}
}
三、定义接口类型(interface.uts)
编写 'utssdk/interface.uts' 文件,定义对外暴露的参数类型和回调风格。uni-app规范使用 success/fail/complete 回调:
export interface Uni {
openAppProduct(options: OpenAppProductOptions): void;
}
export type OpenAppProduct = (options: OpenAppProductOptions) => void;
export type OpenAppProductSuccess = { errMsg: string };
export type OpenAppProductSuccessCallback = (result: OpenAppProductSuccess) => void;
export type OpenAppProductFail = { errMsg: string };
export type OpenAppProductFailCallback = (result: OpenAppProductFail) => void;
export type OpenAppProductComplete = { errMsg: string };
export type OpenAppProductCompleteCallback = (result: OpenAppProductComplete) => void;
export type OpenAppProductOptions = {
success?: OpenAppProductSuccessCallback | null,
fail?: OpenAppProductFailCallback | null,
complete?: OpenAppProductCompleteCallback | null
};
四、实现鸿蒙原生代码(app-harmony/index.uts)
编写 'utssdk/app-harmony/index.uts' 文件,调用两个关键鸿蒙API:bundleManager.getBundleInfoForSelfSync 获取当前应用包名,productViewManager.loadProduct 打开应用市场详情页。注意:productViewManager.loadProduct 本身没有成功回调,只有 error 回调,因此代码通过 isSuccess 标志做“反向判断”。
import { OpenAppProduct, OpenAppProductOptions, OpenAppProductSuccess, OpenAppProductFail, OpenAppProductComplete } from '../interface.uts';
import bundleManager from '@ohos.bundle.bundleManager';
export { OpenAppProduct, OpenAppProductOptions, OpenAppProductSuccess, OpenAppProductFail, OpenAppProductComplete };
import { productViewManager } from '@kit.StoreKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import type { common, Want } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
export function openAppProduct(options: OpenAppProductOptions) {
let isSuccess = true;
try {
const request: Want = {
parameters: {
bundleName: bundleManager.getBundleInfoForSelfSync(
bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT
).name
}
};
productViewManager.loadProduct(
getContext() as common.UIAbilityContext,
request,
{
onError: (err: BusinessError) => {
isSuccess = false;
hilog.info(0, 'TAG', `loadProduct onError. code is ${err.code}, message is ${err.message}`);
let result: OpenAppProductFail = { errMsg: err.message ?? "" };
const completeResult: OpenAppProductComplete = { errMsg: err.message ?? "" };
options?.fail?.(result);
options?.complete?.(completeResult);
}
} as productViewManager.ProductViewCallback
);
} catch (err) {
isSuccess = false;
hilog.error(0, 'TAG', `loadProduct failed. code is ${err.code}, message is ${err.message}`);
let result: OpenAppProductFail = { errMsg: err.message ?? "" };
const completeResult: OpenAppProductComplete = { errMsg: err.message ?? "" };
options?.fail?.(result);
options?.complete?.(completeResult);
}
if (isSuccess) {
let result: OpenAppProductSuccess = { errMsg: "ok" };
const completeResult: OpenAppProductComplete = { errMsg: "ok" };
options?.success?.(result);
options?.complete?.(completeResult);
}
}
五、在页面中使用插件
方式一:挂载到 uni 全局对象。在任意页面引入一次(避免被Tree Shaking摇掉),然后直接调用 uni.openAppProduct()。
<template>
<view class="content">
<button class="button" @click="openAppProductBtn">打开应用市场</button>
</view>
</template>
<script lang="uts">
// 注意:需要在任意页面引入 1 次,否则可能被摇掉
// import "@/uni_modules/xxx-openAppProduct"
export default {
methods: {
openAppProductBtn() {
uni.openAppProduct({
success: (res) => { console.log('success: ', JSON.stringify(res)); },
fail: (err) => { console.error('fail: ', JSON.stringify(err)); },
complete: (res) => { console.log('complete: ', JSON.stringify(res)); }
});
}
}
}
</script>
方式二:直接 import 函数调用,这种方式对Tree Shaking更友好。
<template>
<view class="content">
<button class="button" @click="openAppProductBtn">打开应用市场</button>
</view>
</template>
<script lang="uts">
import { openAppProduct } from "@/uni_modules/xxx-openAppProduct"
export default {
methods: {
openAppProductBtn() {
openAppProduct({
success: (res: any) => { console.log('success: ', JSON.stringify(res)); },
fail: (err: any) => { console.error('fail: ', JSON.stringify(err)); },
complete: (res: any) => { console.log('complete: ', JSON.stringify(res)); }
});
}
}
}
</script>
六、运行到鸿蒙设备
在HBuilderX顶部菜单选择“运行 → 运行到手机或模拟器 → 运行到鸿蒙”。新安装的HBuilderX需先点击“下载真机运行插件”安装运行环境。运行后DevEco Studio自动启动并编译部署。
七、调用鸿蒙第三方ohpm库
在UTS插件中也可以调用鸿蒙的ohpm三方库(如支付宝SDK、推送SDK)。安装ohpm包后,在 'utssdk/app-harmony/*.uts' 文件中import使用。注意:ohpm库不能直接在页面的 'pages/*.vue' 中import,必须通过UTS插件封装。示例:
页面中调用:
import { requestPayment } from "@/uni_modules/test-alipay";
requestPayment({ orderInfo: "xxxx" });
插件实现 'utssdk/app-harmony/index.uts':
import { Pay } from '@cashier_alipay/cashiersdk';
export interface RequestPaymentOptions { orderInfo: string }
export function requestPayment(options: RequestPaymentOptions) {
return new Pay().pay(options.orderInfo, true);
}
八、UTS插件架构概览
项目目录结构:
项目根目录/
├── pages/index/index.vue # 调用方
├── uni_modules/xxx-openAppProduct/ # UTS插件
│ ├── package.json # 配置 arkts: true
│ └── utssdk/
│ ├── interface.uts # 类型定义(跨平台通用)
│ └── app-harmony/index.uts # 鸿蒙原生实现
│ # 还可添加 app-android/、app-ios/ 等平台目录
九、踩坑记录与注意事项
1. 插件名不要以 uni- 开头,避免与官方插件冲突。
2. interface.uts 中定义的类型,在 app-harmony/index.uts 中必须用相对路径 '../interface.uts' 引入。
3. 挂载到 uni 全局对象的插件,需在任意页面 import 一次,否则可能被Tree Shaking摇掉;使用import函数方式则无需此操作。
4. productViewManager.loadProduct 没有成功回调,只能通过 isSuccess 标志“反向判断”——未进入 error 回调即视为成功。这是鸿蒙 API 的设计局限。
5. 第三方ohpm库只能在UTS插件文件中调用,不能在页面 script 中直接import。
6. 真机运行需先安装“真机运行插件”(HBuilderX提示后点击下载),运行会联动 DevEco Studio。
通过以上步骤,你可以在uni-app鸿蒙项目中高效封装UTS插件,调用原生API或第三方库,实现跨平台原生能力复用。
Re: uni-app鸿蒙UTS插件开发实战:调用原生API全流程
感谢楼主分享这么详细的实战教程!正在研究 uni-app 对接鸿蒙原生能力,这篇从创建插件到配置 package.json 再到回调处理的完整流程太实用了。想请教一下,在 `productViewManager.loadProduct` 里用反向判断 `isSuccess` 处理无成功回调的情况,如果遇到加载失败但没触发 error 回调的情况,会不会有遗漏?有什么兜底处理建议吗?Re: uni-app鸿蒙UTS插件开发实战:调用原生API全流程
感谢楼主分享这么详细的UTS插件实战教程!正好最近在搞鸿蒙原生能力调用,这篇从创建到配置再到代码实现的全流程非常清晰,尤其是反向判断成功回调的思路,很实用。 我自己也踩过package.json配置的坑,之前没注意“uni-ext-api”节点,导致一直没法调用原生API,看了你的配置才反应过来。另外有个小疑问:如果插件需要同时兼容安卓和iOS,是不是得在package.json里把kotlin和swift也设成true,然后分别写对应的原生代码文件?还有,interface.uts里定义的OpenAppProductOptions没有传入任何参数,实际调用时是靠内部获取包名,那用户在使用时直接传回调就行了对吧? 期待楼主后续能再讲讲多平台兼容的配置,或者结合其他鸿蒙API的例子就更好了。再次感谢!Re: uni-app鸿蒙UTS插件开发实战:调用原生API全流程
感谢楼主分享这么详细的实战流程!正好最近在踩鸿蒙UTS插件的坑,这篇从创建uni_modules到配置package.json、定义接口再到实现原生调用的全流程,逻辑非常清晰,尤其是对`productViewManager.loadProduct`成功率反向判断的处理,很实用。之前一直没搞懂`isSuccess`标志为什么放try里,看了你的解释就明白了。请问在真机调试时,`bundleManager.getBundleInfoForSelfSync`这一步有没有遇到过权限或签名不一致的问题?期待楼主后续再讲讲其他API的封装经验。
页:
[1]