欢迎到专业群( uni-app x技术交流群 ) 咨询,群中有相关专业的管理员和群友。
好的,这是一个非常专业且切中要害的需求。作为 uni-app x 的开发者,你提出的问题完全触及了跨平台框架在处理高频实时原生交互时的核心矛盾。我将从技术架构、当前实现和未来可能性的角度,逐一拆解并回答你的问题。
首先,直接回答你提出的 5 个具体问题:
1. vapor 是否计划支持 uts 页面逻辑编译为平台原生语言?
目前没有公开的计划。在官方文档中,对 ios 逻辑层的描述是提供了 js 和 swift 双选,但明确提到:“目前 vue 框架还没有 swift 版本,所以如果使用 vue 组件,仍然会编译为 js”。这同样适用于 android。vapor 模式的核心优势在于视图层的极致性能,它将 template 和 style 直接编译为底层 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 构建和更新界面。
-
架构设计
你的页面(.uvue 文件)的 <template> 中只放一个作为容器的 uts 组件插件。所有与 midi、蓝牙、传感器有关的逻辑,以及对应的 ui 更新,全部在这个 uts 组件插件内部用 kotlin/swift/arkts 完成。
-
代码示例 (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生成代码
-
优点与代价
- 优点:性能最高,完全无 js 通信、无序列化、无 gc 抖动,是处理你所述场景的终极方案。
- 代价:放弃了 vue 的声明式开发和热刷新体验,所有 ui 更新都需要命令式地调用原生 api,开发效率较低,且代码与平台强相关。
方案二:混合模式(平衡开发效率与性能)
如果大多数页面逻辑是低频的,只有个别模块是高频的,可以采用此方案。
-
架构设计
页面主体仍然用 vue 的 vapor 模式开发,享受其开发效率。对于 midi 等高频模块,将其封装成一个 uts 组件插件。这个插件内部使用原生 dom api 构建 ui,并在插件内部完成所有高频逻辑处理。插件通过定义明确、低频的自定义事件与页面的 js 逻辑层通信(例如,用户按下一个“保存”按钮,插件向页面发送一个包含当前配置的事件)。
-
代码示例
-
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 系统 中正式提交你的需求,让官方团队听到来自工业控制、实时音频等领域开发者的声音。
4 个回复
Ask小助手
好的,这是一个非常专业且切中要害的需求。作为 uni-app x 的开发者,你提出的问题完全触及了跨平台框架在处理高频实时原生交互时的核心矛盾。我将从技术架构、当前实现和未来可能性的角度,逐一拆解并回答你的问题。
首先,直接回答你提出的 5 个具体问题:
1. vapor 是否计划支持 uts 页面逻辑编译为平台原生语言?
目前没有公开的计划。在官方文档中,对 ios 逻辑层的描述是提供了 js 和 swift 双选,但明确提到:“目前 vue 框架还没有 swift 版本,所以如果使用 vue 组件,仍然会编译为 js”。这同样适用于 android。vapor 模式的核心优势在于视图层的极致性能,它将
template和style直接编译为底层 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 构建和更新界面。
架构设计
你的页面(
.uvue文件)的<template>中只放一个作为容器的 uts 组件插件。所有与 midi、蓝牙、传感器有关的逻辑,以及对应的 ui 更新,全部在这个 uts 组件插件内部用 kotlin/swift/arkts 完成。代码示例 (android 平台)
页面文件 (
pages/midi/midi.uvue):它变得非常薄,只负责引入一个原生驱动的容器。uts 组件插件 (
uni_modules/midi-controller/utssdk/app-android/index.uts)在这里,你使用 kotlin 直接处理 midi 事件,并利用
UniElement等 dom api 直接操作原生视图,完全不存在 js bridge。优点与代价
方案二:混合模式(平衡开发效率与性能)
如果大多数页面逻辑是低频的,只有个别模块是高频的,可以采用此方案。
架构设计
页面主体仍然用 vue 的 vapor 模式开发,享受其开发效率。对于 midi 等高频模块,将其封装成一个 uts 组件插件。这个插件内部使用原生 dom api 构建 ui,并在插件内部完成所有高频逻辑处理。插件通过定义明确、低频的自定义事件与页面的 js 逻辑层通信(例如,用户按下一个“保存”按钮,插件向页面发送一个包含当前配置的事件)。
代码示例
uts 组件插件 (
uni_modules/midi-monitor/utssdk/app-android/index.uts)该插件内部用原生方式处理 midi 数据并更新自己的 ui,只在需要时才与外界通信。
页面文件 (
pages/midi/midi.uvue)总结
你关于
logic-render-target的建议是一个理想的、对开发者友好的方案,但目前官方并未提供。当前,要实现你所需的“无 js 通信开销”的实时场景,可行的路径是将这部分逻辑和 ui 完全封装在 uts 插件中,使用原生 dom api 驱动。这遵循了官方文档中“如果基本不想用 js,可以在 uvue 的 template 中,只放入一个 uts 组件插件,在该插件中使用 uts 来操作 dom”的最佳实践。对于 android vapor 模式,页面逻辑层使用 js 是设计选择,因此调用 uts 插件必然存在跨运行时通信开销。建议持续关注官方文档和更新日志,特别是关于 vapor 模式下 uts 插件标准模式组件支持情况的进展。同时,可以在 dcloud issue 系统 中正式提交你的需求,让官方团队听到来自工业控制、实时音频等领域开发者的声音。
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,应该更适合实时场景。
想请教几个问题:
我个人还是比较希望 Vapor 最终能够保留一种可选的 Native UTS 逻辑模式,比如:
也就是希望能够组合成:
UTS 原生逻辑 + Vapor 原生视图
我不是希望所有项目都必须用 UTS,而是希望官方不要完全放弃 UTS 页面逻辑多端原生编译这条路线。对于普通商城、表单应用,JS 驱动没有太大问题;但 MIDI、实时音频、蓝牙、传感器和工业设备这类场景,对最差延迟和稳定性会更敏感。
如果新版插件通道和直接操作 UniElement 确实能够让高频链路完全绕过 JS,那我觉得也可以解决大部分问题。希望官方后续能把这部分的线程模型、生命周期限制和性能边界说明得更明确一些。
2026-07-14 17:53
DCloud_heavensoft
你的硬件输入是在 C 层还是在 KT 层?如果是 C 层的话,它传到 JS 的通信折损是非常低的。
即便是现在的 UTS 插件通道,也没有线程切换问题,ArrayBuffer 也是共享内存零拷贝的。
新版插件通道主要是解决非 ArrayBuffer 情况下的序列化问题。
在 UTS 插件内部获取到 Element 后,比如说 CanvasElement 后,在 UTS 插件代码里头去调用 Element 或 Canvas API 是完全不经过 JS 的,这个现在也是支持的,而且三个平台都支持。但是我们没有很仔细的测这个场景。如果你使用这个方案且发现问题,可以报 issue 给我们。
操作 UI 元素要回到 UI 主线程。
不过后续会提供 离屏 Canvas 实现子线程也能操作 Canvas,但是操作不了其他 Element。
Dorian (作者)
我的数据源是在 Kotlin 层,问题主要出在通信延时和消费逻辑。
如果把 MIDI 数据的判断、状态计算和 UI 驱动全部放进 UTS 插件,那么 Android、iOS、鸿蒙每一端都要重新实现一套消费逻辑,无法复用一套业务代码,维护成本会很高。
但如果统一放到 JS 层处理,高频 MIDI 事件又可能受到 JS 线程、页面渲染和其他业务任务影响。RN的trubomodules中我也遇到过类似问题:Kotlin 层采集数据没有压力,通信本身也很快,但事件进入 JS 后仍然可能出现堆积和延迟。
所以我真正难解决的是:既希望消费逻辑能够跨端复用,又希望它具备接近原生层的高频处理能力。离屏 Canvas 可以解决部分绘制问题,但无法解决整套跨端业务消费逻辑应该运行在哪里的问题。
2026-07-17 19:47
DCloud_heavensoft
回复 Dorian: uts插件里,可以写uts代码,3端一套代码。从midi采数是各平台原生封装的,然后到了utssdk下的uts代码中,uts代码拿到数据,比如arraybuffer,同时通过uni.getElementById拿到uvue界面上的UNIElement(先在uvue创建好组件),更新UI,这样从数据到UI的逻辑,是一套uts代码。
uts新插件通道的技术还是很强的,加上arraybuffer共享内存,应该也不会在js层造成堆积,过段时间我们发版你可以试下。
2026-07-18 07:06
DCloud_heavensoft
当然也只有这段高频更新数据在uts插件中,其他逻辑还在uvue页面中
2026-07-18 07:07
Dorian (作者)
回复 DCloud_heavensoft: 这个方案能解决眼前的性能问题,但从长期来看,我更期待逻辑层和插件层真正分开。
插件层只负责封装各端原生能力,逻辑层则用一套 UTS 代码承载数据处理和业务逻辑。否则插件最终很容易从“能力适配层”逐渐变成“业务逻辑层”。
目前还没有哪个跨端框架真正做到一套逻辑多端编译并原生运行。UTS 如果能把逻辑层也走通,价值就不只是插件性能更高,而是可能形成一条新的跨端技术路线。
2026-07-18 14:01
choin
强烈建议在vapor中可以uts中混写各平台原生代码的方式。
我想大多数开发者都希望如此,目前是如果开启了vapor,那么需要将各平台的原生代码全部从uni_modules中单独创建一个文件夹,那我现在有一个助手库(这个库很多项目在用),里面涵盖了组件、页面、函数、各平台原生代码, 本来我可以很方便的在一个目录更新的,但是现在却要将原生部分移出去,本来可以一个目录复制到各个项目,现在却要看看哪个uni_modules的文件夹是否需要复制? 那么多项目可想而知这很不方便。
DCloud_heavensoft
没get到。抽到uni_modules里,不是更方便每个项目都加载和更新这个uni_modules的最新版吗?
2026-07-23 03:03
choin
回复 DCloud_heavensoft:
助手库结构:
uni_modules/helper主入口,负责调度
uni_modules/helper/components组件库
uni_modules/helper/util助手文件(之前在这里面有混写原生)
uni_modules/helper/utssdk混编文件(现在挪到这里调用报错)
现在需要把原生的单独摘出来,新建uni_modules/native,然后再从uni_modules/helper导入?
希望的是一个目录下能完成
2026-07-23 10:37
choin
我测试了一下,在一个 helper目录下内的.uts调用utssdk是无法编译的。
之前是原生代码放在需要的.uts中,现在需要原生代码抽出来按平台分类放到utssdk,但是一个目录(helper)下却不能完成编译。
也可能原因是“uni_modules/helper主入口”是在page中调用的原因,但如果是这样那好不方便,那我需要单独去从页面调用utssdk? 我想要的是能集成到helper方法库中,而不是零散的在页面中,就像php后端有vendor包目录,你可以很方便的进行更新,而且每个包内都可以自己调用自己。
2026-07-23 10:43
要回复问题请先登录或注册
公告
更多>相关问题