查看: 78|回复: 3

uni-app鸿蒙原生组件集成:UTS插件注册ArkTS组件实践

[复制链接]
发表于 1 小时前 | 显示全部楼层 |阅读模式
在 uni-app 开发鸿蒙应用时,Vue 组件在复杂 UI 场景下(如地图、扫码、自定义绘制)往往性能不足或功能受限。从 HBuilderX 4.62 版本开始,uni-app 提供了通过 UTS 插件注册鸿蒙原生组件的方案,允许在 Vue 页面中使用 <embed> 标签直接渲染 ArkTS 组件,并支持同层渲染——原生组件能与普通 Vue 组件混合布局,无需额外层级处理。

整体架构分为三层:Vue 页面通过 <embed> 传递属性和事件;UTS 插件负责桥接,导入 .ets 文件;ArkTS 层执行真正的原生渲染。这种设计让前端开发者只需关注 Vue 模板语法,无需手动编写通道通信代码,相比 Flutter 的原生集成方案更友好。

一、注册鸿蒙原生组件的核心 API
注册过程主要依赖 defineNativeEmbed 函数,其类型签名如下:
  1. export interface NativeEmbedEvent { detail: object; }
  2. export interface NativeEmbedBuilderOptions { width: number; height: number; on?: Map<string, (event?: NativeEmbedEvent) => void>; }
  3. export interface DefineNativeEmbedOptions<T extends NativeEmbedBuilderOptions = NativeEmbedBuilderOptions> { builder: (options: T) => void; }
  4. export declare function defineNativeEmbed<T extends NativeEmbedBuilderOptions = NativeEmbedBuilderOptions>(tag: string, options: DefineNativeEmbedOptions<T>): void;
复制代码

关键参数说明:
tag:组件标签名,必须小写,不支持驼峰命名(例如 'button' 而不是 'myButton'),否则可能无法正常渲染。
builder:构建函数,接收 NativeEmbedBuilderOptions 对象,包含 width、height 以及事件回调 Map(key 为事件名,小写)。

二、实战:注册一个鸿蒙原生按钮
在 UTS 插件的 app-harmony/ 目录下创建 button.ets 文件。首先定义一个标准的 ArkTS 组件 ButtonComponent,它接收 label 属性和 onButtonClick 回调:
  1. import { NativeEmbedBuilderOptions, defineNativeEmbed } from "@dcloudio/uni-app-runtime";
  2. interface ButtonBuilderOptions extends NativeEmbedBuilderOptions { label: string; }
  3. interface ButtonClickEventDetail { text: string; }
  4. @Component
  5. struct ButtonComponent {
  6.   @Prop label: string;
  7.   onButtonClick?: Function;
  8.   build() {
  9.     Button(this.label)
  10.       .width('100%')
  11.       .height('100%')
  12.       .onClick(() => {
  13.         if (this.onButtonClick) {
  14.           const detail = { text: 'test' } as ButtonClickEventDetail;
  15.           this.onButtonClick({ detail });
  16.         }
  17.       });
  18.   }
  19. }
  20. @Builder
  21. function ButtonBuilder(options: ButtonBuilderOptions) {
  22.   ButtonComponent({
  23.     label: options.label,
  24.     onButtonClick: options?.on?.get('buttonclick')
  25.   })
  26.   .width(options.width)
  27.   .height(options.height);
  28. }
  29. defineNativeEmbed('button', {
  30.   builder: ButtonBuilder
  31. });
复制代码

这段代码的关键逻辑:
- ButtonComponent 通过 @Prop 接收 label,并通过 onButtonClick 回调传递点击事件。
- @Builder 函数 ButtonBuilder 将 options 中的 width、height 传递给组件,并从事件 Map 中取出 'buttonclick' 事件的回调。
- defineNativeEmbed 将该组件注册为标签名 'button' 的原生组件。

三、在 UTS 插件中导入 .ets 文件
编辑或创建 UTS 插件根目录下的 app-harmony/index.uts 文件,只需要引入 .ets 文件即可触发注册:
  1. import './button.ets';
复制代码

注意:不需要导出 .ets 中的任何内容,因为 defineNativeEmbed 在执行时已经全局注册。.ets 文件必须放在 app-harmony/ 目录下,不能在 utssdk/ 或其他目录——utssdk/ 只存放纯 TypeScript 接口定义。

四、在 Vue 页面中使用原生组件
注册完成后,在 Vue 页面通过 <embed> 标签调用,并传入 options 属性和事件监听:
  1. <template>
  2.   <embed class="native-button" tag="button" :options="options" @buttonclick="onClick"></embed>
  3. </template>
  4. <script>
  5. export default {
  6.   data() { return { options: { label: 'hello' } }; },
  7.   methods: {
  8.     onClick(e) { console.log('onClick', e.detail.text); this.options = { label: 'world' }; }
  9.   }
  10. };
  11. </script>
  12. <style scoped>
  13. .native-button { display: block; width: 200px; height: 50px; margin: 10px auto; }
  14. </style>
复制代码

更新 options 时需要注意:必须对整个 options 对象重新赋值(例如 this.options = { label: 'new value' }),而不是修改已有对象的属性(如 this.options.label = 'new value' 不会触发更新)。这是因为 embed 组件通过引用变化来判断是否需要重绘原生层。

五、事件转发机制
所有通过 @ 绑定在 <embed> 组件上的事件(如 @buttonclick)都会自动转发到原生层。在 ArkTS 代码中通过 options.on?.get('事件名') 接收。事件名必须完全一致,建议统一使用小写格式。例如 Vue 端使用 @buttonclick,原生端就用 'buttonclick'。

六、在 WebView 之上展示原生组件
如果页面包含 WebView 组件,需要让原生组件覆盖在 WebView 之上(例如悬浮按钮),可以在 defineNativeEmbed 中设置 nodeRenderType 参数:
  1. import { NodeRenderType } from '@kit.ArkUI';
  2. defineNativeEmbed('button', {
  3.   builder: ButtonBuilder,
  4.   nodeRenderType: NodeRenderType.RENDER_TYPE_DISPLAY
  5. });
复制代码

NodeRenderType.RENDER_TYPE_DISPLAY 使组件以 Display 模式渲染,可覆盖在 WebView 内容之上。注意:如果页面没有使用 WebView,不要设置此属性,否则可能影响普通页面中的组件表现。

七、踩坑记录总结
1. 标签名必须全小写,禁止驼峰。
2. options 更新必须重新赋值整个对象,不能直接修改属性。
3. 事件名大小写敏感,Vue 端和原生端必须完全一致(建议全小写)。
4. .ets 文件只能放在 app-harmony/ 目录下。
5. 导入 .ets 文件只需 import 副作用,无需 export。
6. nodeRenderType 仅在需要覆盖 WebView 时使用,普通页面无需设置。

社区已有基于该方案的 UTS 插件案例,例如“UTS 组件鸿蒙花瓣地图”和“鸿蒙粘贴控件”,可在插件市场获取完整示例项目(插件 ID:23082、24925),这些案例演示了原生地图组件和剪切板控件的完整集成流程。

通过这套方案,开发者可以在 uni-app 中高效复用鸿蒙原生能力,既享受跨端框架的便利,又能获得原生组件的性能与功能。
回复

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: uni-app鸿蒙原生组件集成:UTS插件注册ArkTS组件实践

感谢楼主的详细分享!这个通过UTS插件注册ArkTS组件的方案确实很实用,特别是同层渲染解决了原生组件与Vue组件混合布局的痛点。我之前尝试在uni-app里集成地图组件时性能一直不理想,看了这篇实践感觉可以试试用&LT;embed&GT;标签替代原来的方案了。有几个问题想请教:定义ButtonBuilder时,事件名必须全小写吗?比如onClick映射成'buttonclick',如果ArkTS组件有多个回调,是不是都要小写后放进Map里?另外,如果原生组件需要动态更新options,比如label变化,Vue端直接修改data里的options值就能触发重渲染吗?期待楼主能再补充一下性能方面的注意事项,或者有没有遇到过底层ArkTS组件异常导致整个页面崩溃的情况?再次感谢!
回复 支持 反对

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: uni-app鸿蒙原生组件集成:UTS插件注册ArkTS组件实践

感谢楼主分享这么详细的教程!之前一直困惑 uni-app 里怎么高效集成鸿蒙原生组件,这篇文章把架构和核心 API 讲得很清楚,尤其是 `defineNativeEmbed` 和 `@Builder` 的配合方式,比我想象的要简洁。我有个疑问:如果要在原生组件里传递复杂对象(比如地图的经纬度数组或者自定义绘制参数),options 属性是否支持嵌套对象?还是说需要自己序列化成字符串再传?期待楼主后续能再讲讲类似地图、扫码这种复杂场景的实际案例。
回复 支持 反对

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: uni-app鸿蒙原生组件集成:UTS插件注册ArkTS组件实践

这篇文章写得很详细,对想尝试在 uni-app 中集成鸿蒙原生组件的开发者帮助很大。特别是 `defineNativeEmbed` 的用法和 `.ets` 文件的放置要求,讲得很清楚。不过我在想,这种方式在复杂 UI(比如嵌套多个原生组件或动态更新属性)时,性能和稳定性表现如何?有没有实际项目中的坑能分享一下?谢谢!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-7-17 20:52 , Processed in 0.022877 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部