先说一个正在发生的变化
如果你正在考虑写 uni-app 原生插件,先停下来看一眼时间。
2024 年 8 月 1 日起,uni-app 插件市场不再接收新的 App 原生语言插件。官方文档已经明确标注:App 原生语言插件已不再维护,请改用 uts 插件 。
这意味着什么?如果你现在新建一个项目,从零开始写 Java/Object-C 原生语言插件,你选了一条正在被淘汰的路。但"原生插件"这个需求本身没有消失------你需要的能力还在,只是实现方式变了。
uni-app 的"够用"边界在哪里
uni-app 封装了大量跨端 API,覆盖了 80% 的常规业务场景。但它的能力边界是清晰的:一切需要直接触碰操作系统底层或特定硬件的能力,它都没有。
举几个真实的例子:
获取当前 WiFi 的 SSID 。uni.getNetworkType 只能告诉你"当前是 WiFi",但拿不到 WiFi 名称。想要 SSID,必须走原生。
集成第三方音视频 SDK 。腾讯云、声网、融云的 SDK 都是原生级别的,它们的采集、编码、渲染跑在原生线程上。你不可能让 WebView 里的 JavaScript 去驱动摄像头做实时美颜------帧率和延迟都不允许。
调用特定硬件传感器。计步器、心率传感器、工业蓝牙设备,这些都没有跨端标准 API。
需要嵌入的原生 UI 组件 。比如在页面局部嵌入一个原生地图、原生相机预览。注意:uni-app 的 vue 页面只支持 Module 类型插件 (无 UI 的能力扩展),想要嵌入原生 UI 组件,页面必须是 nvue 。
所以原生插件的本质是:uni-app 没封装的,你通过原生代码自己封装一个桥接层,让 JavaScript 能调用。
2026 年的正确姿势:uts,不是 Java
先说结论:新项目,写 uts 插件。原生语言插件只在你需要维护历史代码时才碰。
uts 是 DCloud 推出的"原生扩展语言",语法上接近 TypeScript,但编译到 Android 是 Kotlin,到 iOS 是 Swift。你写的是 uts,跑的是真正的原生代码,但开发体验接近写前端。
为什么 uts 是现在该用的方式:
免审核 。原生语言插件上架插件市场需要官方人工审核,快则一天慢则未知。uts 插件免审,自己用或者发市场都快得多。
写原生代码的门槛大幅降低。你不需要配置 Android Studio、搞 Gradle、处理 Xcode 工程文件。uts 在 HBuilderX 里就能写、能调、能编译。
一套代码同时覆盖 Android/iOS/HarmonyOS 。原生语言插件 Android 和 iOS 要分别写 Java 和 Object-C,uts 写一次编译到两端。
能直接调用原生库 。uts 编译到 Kotlin/Swift,你可以直接 import 原生的类和方法。比如调用 Android 的 TextUtils、iOS 的 ProcessInfo,语法是统一的 uts。
什么时候才需要写原生语言插件?
只有一种情况:你的现有项目已经深度绑定了原生语言插件,且短期无法迁移。除此之外,新开发一律 uts。
怎么写一个 uts 插件(最小可用示例)
假设你要做一个能力:获取设备可用内存。uni-app 没有这个 API,我们来封装一个 uts 插件。
第一步:理解两种插件形态
Module 模式(API 型) :没有内嵌 UI,或者只有全屏弹出 UI。调用方式类似 uni.requireNativePlugin 后调方法。这是最常用的。
Component 模式(组件型) :在页面中以内嵌标签形式存在的原生 UI。比如 <my-camera-view>。在 uni-app 中,Component 模式只能在 nvue 页面使用 。如果你写的是 vue 页面,只能做 Module。
第二步:写一个 Module 插件
目录结构大致如下(放在 uni_modules 下):
text
go
uni_modules/
└── memory-info/
├── utssdk/
│ ├── app-android/
│ │ └── index.uts
│ ├── app-ios/
│ │ └── index.uts
│ └── interface.uts
└── package.json
interface.uts 定义对外的接口类型:
uts
typescript
export type GetMemoryInfo = () => number[]
export type OnMemoryInfoChange = (callback: (res: number[]) => void) => void
app-android/index.uts 用 Kotlin 能力实现:
uts
javascript
import { GetMemoryInfo, OnMemoryInfoChange } from '../interface.uts'
// 直接调用 Android 原生 API
export const getMemoryInfo: GetMemoryInfo = function () : number[] {
const activity = UTSAndroid.getUniActivity()
const memoryInfo = new ActivityManager.MemoryInfo()
const activityManager = activity!.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
activityManager.getMemoryInfo(memoryInfo)
const totalMem = memoryInfo.totalMem / 1024 / 1024
const availMem = memoryInfo.availMem / 1024 / 1024
return [Number.from(totalMem), Number.from(availMem)]
}
app-ios/index.uts 用 Swift 能力实现(写法类似,调用 ProcessInfo 等原生类)。
第三步:在前端调用
javascript
javascript
import { getMemoryInfo } from '@/uni_modules/memory-info'
const mem = getMemoryInfo()
console.log('总内存:', mem[0], 'MB', '可用:', mem[1], 'MB')
注意:uts 插件不像老的 uni.requireNativePlugin 那样运行时动态加载。它是编译期集成的,你直接 import 就行,类型提示都有。
旧方式:原生语言插件还要不要了解
了解即可,不要新建。
如果你维护的是历史项目,或者在插件市场买了老的原生语言插件(云端插件),用法是:
javascript
ini
const PluginName = uni.requireNativePlugin('DCloud-RichAlert')
PluginName.show({...})
调试时必须制作自定义基座 ,因为标准基座不含这些插件。云端插件购买后绑定 appid,云打包时直接合并。本地插件放在 nativeplugins/ 目录,配置到 manifest.json 里。
但所有这些,新项目都不要再走了。
什么场景该自己写,什么场景该找现成的
先搜插件市场。付费的也可以考虑。一个成熟的原生插件往往经过大量设备验证,稳定性比你自己写的初版好得多。音视频、推送、地图、支付这些通用能力,大概率已经有现成的。
自己写的唯一充分理由:你的需求足够特殊,市场上没有,或者现有插件不满足关键指标(性能、包体积、兼容性)。
比如那个水印相机的案例:美颜 SDK 需要独立渲染线程、时间戳对齐、动态码率调整。这种东西没有通用插件能覆盖,必须针对自己的业务场景封装。
一句话选型
新项目写 uts,旧项目维护老插件,需求特殊才自己动手,通用能力先看市场。 别在 2026 年从零开始写 Java 原生语言插件。