Dorian
Dorian
  • 发布:2026-07-14 17:26
  • 更新:2026-07-14 17:32
  • 阅读:51

【需求反馈】建议 uni-app x Vapor 保留 UTS 逻辑层原生编译模式

分类:uni-app x

建议 Vapor 保留 UTS 逻辑层原生编译模式

问题背景

uni-app x 最初的重要技术优势是:UTS 在 Android、iOS、鸿蒙平台分别编译为 Kotlin、Swift、ArkTS,从而避免 JavaScript 逻辑层与原生层之间的频繁通信。

目前 Android Vapor 模式采用 JS 驱动后,虽然视图层通过 Vapor 编译获得了较高的渲染性能,但涉及 MIDI、音频、蓝牙、传感器、工业设备等高频实时场景时,原生事件仍可能需要经过:

原生 API → UTS 插件 → JS 逻辑层 → Vapor UI

这重新引入了跨运行时通信、JS 事件队列阻塞和垃圾回收抖动等风险。

建议方案

希望 Vapor 模式长期支持两种逻辑层编译目标:

  1. JS/TS 逻辑模式
    用于兼容旧 uni-app 项目、npm 生态和普通业务应用。

  2. Native UTS 逻辑模式
    <script setup lang="uts"> 分别编译为 Kotlin、Swift、ArkTS,同时继续使用 Vapor 原生视图编译能力。

理想架构:

UTS 原生逻辑编译
+ Vapor 无 VDOM 原生视图
+ UTS 原生插件

建议允许通过项目、页面或组件配置选择逻辑层目标,例如:

{
"uni-app-x":{
"vapor": true,
"logic-render-target": "native"
}
}

希望官方明确的问题

  1. Vapor 是否计划支持 UTS 页面逻辑编译为平台原生语言?
  2. Android Vapor 中 JS 调用 UTS 插件采用什么通信机制?
  3. 是否存在序列化、线程切换或事件队列开销?
  4. Android VDOM 的 UTS/Kotlin 原生逻辑模式是否会长期维护?
  5. 是否考虑提供 JS 兼容模式与 Native UTS 模式并存的方案?

该能力对于 MIDI、实时音频、游戏输入、蓝牙、串口、传感器和工业控制类应用具有重要价值。

2026-07-14 17:26 负责人:无 分享
已邀请:
Ask小助手

Ask小助手

欢迎到专业群( uni-app x技术交流群 ) 咨询,群中有相关专业的管理员和群友。

好的,这是一个非常专业且切中要害的需求。作为 uni-app x 的开发者,你提出的问题完全触及了跨平台框架在处理高频实时原生交互时的核心矛盾。我将从技术架构、当前实现和未来可能性的角度,逐一拆解并回答你的问题。

首先,直接回答你提出的 5 个具体问题:

1. vapor 是否计划支持 uts 页面逻辑编译为平台原生语言?
目前没有公开的计划。在官方文档中,对 ios 逻辑层的描述是提供了 js 和 swift 双选,但明确提到:“目前 vue 框架还没有 swift 版本,所以如果使用 vue 组件,仍然会编译为 js”。这同样适用于 android。vapor 模式的核心优势在于视图层的极致性能,它将 templatestyle 直接编译为底层 c 代码对应的机器码/字节码,从而绕过了 vnode 的开销。然而,逻辑层(即 <script setup> 中的代码)在 android vapor 模式下,是被编译为 js 运行的。官方明确说明,android vapor 模式兼容 js/ts 写法,正是为了避免开发者必须使用强类型的 uts。这意味着,在当前架构下,vapor 模式的设计选择是“高性能原生视图 + js 逻辑驱动”。

2. android vapor 中 js 调用 uts 插件采用什么通信机制?
这回到了 uni-app x 架构的核心优势。uts 插件本身就是原生代码,在 android 上编译为 kotlin。当你在 vapor 模式的页面(js 逻辑层)中调用一个 uts 插件时,确实会发生从 js 运行时到原生(kotlin)的跨语言通信。这个通信过程涉及到序列化、线程切换和事件队列,这正是你担忧的根源。

3. 是否存在序列化、线程切换或事件队列开销?
是的,存在。 这正是你需求反馈的价值所在。一旦逻辑层是 js,它调用任何原生能力(无论是系统 api 还是 uts 插件)都必须经过 js bridge。对于 midi、高频传感器数据(例如每几毫秒一次的回调)这类场景,这种开销是完全不可接受的。官方文档在比较 flutter 时也指出了类似问题:“dart 并不能直接调用 kotlin 的 class,只能先序列化成字符串...而在 uni-app x 中,这种跨语言通信是不存在的...uts 在 android 上就编译为了 kotlin”。但这句话的前提是逻辑层本身就是 kotlin。如果逻辑层是 js,那么通信开销就会重新出现。

4. android vdom 的 uts/kotlin 原生逻辑模式是否会长期维护?
根据官方的发展路线,vapor 模式正在逐步替代老的 vdom 模式。文档指出:“从 2026 年起,新的蒸汽模式将逐渐替代老的 vdom 模式”。对于 android 平台,vapor 模式在 hbuilderx 5.21 版本才推出,并且官方推荐开发者使用兼容 js/ts 的 vapor 模式。因此,vdom 模式,尤其是其 uts/kotlin 原生逻辑编译模式,虽然目前仍可用,但不太可能是长期投入和维护的重点。未来的研发资源会集中在 vapor 模式上。

5. 是否考虑提供 js 兼容模式与 native uts 模式并存的方案?
这正是你提出的 logic-render-target 配置的核心诉求。目前官方没有提供该配置。官方推荐的、在 ios 上实现“纯原生驱动”的现有方案是:在 uts 插件中直接使用 dom api 操作界面,完全绕开 vue 框架。这种方式下,uts 插件编译为 swift/kotlin,实现了逻辑层和视图层的双原生,没有 js 通信开销。但代价是无法使用 vue 的响应式和数据绑定等便捷特性。


针对你需求的技术分析与可行方案

你的理想架构非常清晰,但目前官方并未提供从页面配置层面切换逻辑层编译目标的选项。结合你的场景,我提供两种切实可行的技术路径:

方案一:全原生驱动(推荐用于性能极致场景)

这是官方当前支持的、能完全规避 js 通信开销的方案。核心思想是:将高频实时逻辑和 ui 操作全部下沉到 uts 插件中,使用原生 dom api 构建和更新界面。

  1. 架构设计
    你的页面(.uvue 文件)的 <template> 中只放一个作为容器的 uts 组件插件。所有与 midi、蓝牙、传感器有关的逻辑,以及对应的 ui 更新,全部在这个 uts 组件插件内部用 kotlin/swift/arkts 完成。

  2. 代码示例 (android 平台)

    • 页面文件 (pages/midi/midi.uvue):它变得非常薄,只负责引入一个原生驱动的容器。

      <template>  
        <view style="flex: 1;">  
          <!-- 这是一个 uts 标准模式组件插件,内部完全由 kotlin 驱动 -->  
          <midi-controller ref="midiController" />  
        </view>  
      </template>  
      
      <script setup lang="uts">  
      // 页面逻辑层几乎为空,或者只处理非实时、低频的交互  
      </script>  

      本助手使用国产模型,生成代码准确率较低。强烈建议使用uni-agent生成代码

    • uts 组件插件 (uni_modules/midi-controller/utssdk/app-android/index.uts)
      在这里,你使用 kotlin 直接处理 midi 事件,并利用 UniElement 等 dom api 直接操作原生视图,完全不存在 js bridge。

      // 在插件的 NVLoad 生命周期中创建原生视图和绑定逻辑  
      override NVLoad(): FrameLayout {  
          val rootLayout = FrameLayout($androidContext)  
      
          // 1. 创建原生 UI,例如一个 TextView 来显示 midi 音符  
          val noteTextView = TextView($androidContext)  
          noteTextView.text = "等待 midi 信号..."  
          noteTextView.textSize = 24.0f  
          val params = FrameLayout.LayoutParams(  
              FrameLayout.LayoutParams.WRAP_CONTENT,  
              FrameLayout.LayoutParams.WRAP_CONTENT  
          )  
          params.gravity = Gravity.CENTER  
          rootLayout.addView(noteTextView, params)  
      
          // 2. 在这里直接调用原生 midi api(伪代码)  
          // val midiManager = context.getSystemService(Context.MIDI_SERVICE) as MidiManager  
          // midiManager.devices.forEach { device ->  
          //     // 打开设备,注册回调  
          //     midiManager.openDevice(device, { midiData ->  
          //         // 高频回调直接在主线程更新原生视图,零通信开销  
          //         noteTextView.text = "note: ${midiData.note}"  
          //     }, null)  
          // }  
      
          return rootLayout  
      }  

      本助手使用国产模型,生成代码准确率较低。强烈建议使用uni-agent生成代码

  3. 优点与代价

    • 优点:性能最高,完全无 js 通信、无序列化、无 gc 抖动,是处理你所述场景的终极方案。
    • 代价:放弃了 vue 的声明式开发和热刷新体验,所有 ui 更新都需要命令式地调用原生 api,开发效率较低,且代码与平台强相关。

方案二:混合模式(平衡开发效率与性能)

如果大多数页面逻辑是低频的,只有个别模块是高频的,可以采用此方案。

  1. 架构设计
    页面主体仍然用 vue 的 vapor 模式开发,享受其开发效率。对于 midi 等高频模块,将其封装成一个 uts 组件插件。这个插件内部使用原生 dom api 构建 ui,并在插件内部完成所有高频逻辑处理。插件通过定义明确、低频的自定义事件与页面的 js 逻辑层通信(例如,用户按下一个“保存”按钮,插件向页面发送一个包含当前配置的事件)。

  2. 代码示例

    • uts 组件插件 (uni_modules/midi-monitor/utssdk/app-android/index.uts)
      该插件内部用原生方式处理 midi 数据并更新自己的 ui,只在需要时才与外界通信。

      // 插件内部逻辑...  
      override NVLoad(): FrameLayout {  
          // ... 创建原生视图 ...  
          // ... 注册 midi 回调,高频更新原生视图 ...  
      
          // 当需要通知页面时,发送低频自定义事件  
          // this.$emit("midi-device-connected", deviceInfo)  
          return rootLayout  
      }  

      本助手使用国产模型,生成代码准确率较低。强烈建议使用uni-agent生成代码

    • 页面文件 (pages/midi/midi.uvue)

      <template>  
        <view style="flex: 1;">  
          <!-- 其他用 vue 写的低频率 UI -->  
          <text>当前状态: {{ status }}</text>  
      
          <!-- 高频的原生驱动组件 -->  
          <midi-monitor @midi-device-connected="onDeviceConnected" />  
      
          <button @click="saveConfig">保存配置</button>  
        </view>  
      </template>  
      
      <script setup lang="uts">  
      const status = ref("等待设备...")  
      
      const onDeviceConnected = (deviceInfo : UTSJSONObject) => {  
          // 这是一个低频事件,跨语言通信的开销可以忽略  
          status.value = "已连接: " + deviceInfo.getString("name")  
      }  
      
      const saveConfig = () => {  
          // ... 低频操作 ...  
      }  
      </script>  

      本助手使用国产模型,生成代码准确率较低。强烈建议使用uni-agent生成代码

总结

你关于 logic-render-target 的建议是一个理想的、对开发者友好的方案,但目前官方并未提供。当前,要实现你所需的“无 js 通信开销”的实时场景,可行的路径是将这部分逻辑和 ui 完全封装在 uts 插件中,使用原生 dom api 驱动。这遵循了官方文档中“如果基本不想用 js,可以在 uvue 的 template 中,只放入一个 uts 组件插件,在该插件中使用 uts 来操作 dom”的最佳实践。对于 android vapor 模式,页面逻辑层使用 js 是设计选择,因此调用 uts 插件必然存在跨运行时通信开销。

建议持续关注官方文档和更新日志,特别是关于 vapor 模式下 uts 插件标准模式组件支持情况的进展。同时,可以在 dcloud issue 系统 中正式提交你的需求,让官方团队听到来自工业控制、实时音频等领域开发者的声音。

内容为 AI 生成,仅供参考
DCloud_heavensoft

DCloud_heavensoft

官方目前正在开发新版uts插件通道底层,新版插件通道将避免序列化,并且兼容之前的插件协议。

另外在uts插件中,也支持操作dom,蓝牙拿到数据后,可以在kt层直接写UniElement。后者目前有些坑,如遇到问题报给我们,我们优先处理。

另外,官方还在做wasm支持,届时c代码的插件可以更高性能的和js交互。

如果这些改进能解决性能问题,可能就不在页面中支持uts2kt驱动了。如果不能,也会补充。

  • Dorian (作者)

    感谢回复,我大概理解官方现在的思路了:先通过新版 UTS 插件通道、UTS 直接操作 UniElement,以及后续的 Wasm 支持,尽量解决 JS 和原生之间的性能问题。如果这些方案能满足需求,就可能暂时不在 Vapor 页面中增加 uts2kt 驱动。


    我这边主要想补充一下自己的实际场景和诉求。


    我的项目是跨平台音乐教育应用,其中有虚拟钢琴功能。真实电子钢琴会通过 MIDI 持续传入 Note On、Note Off、力度、延音踏板等数据,应用需要马上更新虚拟琴键、处理音符状态、驱动音频,并进行节奏和错音判断。


    我比较担心的是,如果链路是:


    原生 MIDI → UTS 插件 → JS 回调 → Vue 状态更新 → Vapor UI


    即使新版插件通道取消了序列化,原生事件最终还是进入 JS 线程。假如 JS 当时正在处理其他同步逻辑、页面渲染、JSON 或网络回调,琴键反馈是否仍然可能延迟?


    所以我最关心的是 UTS 插件直接操作 UniElement 这条路线。


    如果可以做到:


    原生 MIDI → Kotlin/Swift/ArkTS 状态机 → 直接更新 UniElement


    那高频 MIDI 数据就不需要经过 JS,应该更适合实时场景。


    想请教几个问题:



    1. 新版 UTS 插件通道除了避免序列化外,是否还会减少线程切换和数据复制?

    2. ArrayBuffer、TypedArray 这类二进制数据是否支持零拷贝?

    3. 原生回调进入 JS 后,是否仍然需要等待 JS 事件循环?

    4. UTS 插件能否长期持有 UniElement,并在页面存活期间持续更新?

    5. 原生侧操作 UniElement 时,是否必须切换到 UI 线程?

    6. 是否支持一次批量更新多个琴键,而不是逐个修改?

    7. 原生直接修改 UniElement 后,会不会和 Vue 响应式状态产生冲突?

    8. Android、iOS、鸿蒙后续都会支持相同的能力吗?

    9. Wasm 后续是否会支持多线程、SIMD、共享内存或 Worker?

    10. 官方是否会针对 MIDI、蓝牙、传感器这类高频场景提供性能测试数据?


    我个人还是比较希望 Vapor 最终能够保留一种可选的 Native UTS 逻辑模式,比如:



    • 普通 JS/TS 页面继续使用 JS 驱动,方便迁移 uni-app 和使用 npm 生态;

    • 对实时性要求高的页面或组件,允许 UTS 继续编译成 Kotlin、Swift、ArkTS;

    • 同时继续使用 Vapor 的无 VDOM 原生视图能力。


    也就是希望能够组合成:


    UTS 原生逻辑 + Vapor 原生视图


    我不是希望所有项目都必须用 UTS,而是希望官方不要完全放弃 UTS 页面逻辑多端原生编译这条路线。对于普通商城、表单应用,JS 驱动没有太大问题;但 MIDI、实时音频、蓝牙、传感器和工业设备这类场景,对最差延迟和稳定性会更敏感。


    如果新版插件通道和直接操作 UniElement 确实能够让高频链路完全绕过 JS,那我觉得也可以解决大部分问题。希望官方后续能把这部分的线程模型、生命周期限制和性能边界说明得更明确一些。

    2026-07-14 17:53

要回复问题请先登录注册