Chapter 09

uni-app x 与 UTS 原生扩展

掌握 UTS 语言核心语法,直接调用 Android/iOS 原生 API,开发高性能跨端插件

9.1 UTS 语言核心设计理念

UTS(Uni Type Script)是 DCloud 专为 uni-app x 设计的编译型语言。它的核心设计目标是:让前端开发者用接近 TypeScript 的语法,直接调用原生平台的 API,并获得原生代码的性能

UTS 与 TypeScript 的关键区别:

特性TypeScriptUTS
运行时JS 引擎(V8/JSCore)原生运行时(ART/Swift)
编译产物JavaScriptKotlin / Swift / JS(按平台)
npm 生态完整支持不支持(需专门适配)
原生 API 调用通过 Bridge 间接调用直接调用,零开销
类型系统结构化类型(兼容)名义类型(更严格)
语法兼容90% 兼容 TypeScript 语法

9.2 UTS 语法详解

基础类型系统

UTS 的类型系统比 TypeScript 更接近强类型语言:

// 基本类型(与 TypeScript 一致)
let count: number = 42
let name: string = 'hello'
let active: boolean = true
let data: any = null    // 谨慎使用 any

// 平台特有类型(Android 编译时有效)
let context: android.content.Context
let bitmap: android.graphics.Bitmap

// UTS 的可空类型(重要!)
let maybeNull: string | null = null
let result = maybeNull?.length    // 可选链访问
let len = maybeNull ?? 0          // 空值合并

// 数组
const nums: number[] = [1, 2, 3]
const strs: Array<string> = ['a', 'b']

// 接口(与 TypeScript 一致)
interface Point {
  x: number
  y: number
  label?: string
}

// 类(支持继承和接口实现)
class Circle {
  radius: number

  constructor(radius: number) {
    this.radius = radius
  }

  area(): number {
    return Math.PI * this.radius ** 2
  }
}

UTS 的特殊语法

UTS 引入了一些 TypeScript 中没有的语法,用于与原生平台交互:

// as 类型转换(强制转换,类似 Kotlin 的 as)
const view = UTSAndroid.getContext().findViewByTag(100) as android.view.TextView

// instanceof 类型判断
if (view instanceof android.view.Button) {
  // 在此作用域内,view 被自动推断为 Button 类型
  view.setText("点击")
}

// 平台特有的 lambda/闭包语法(编译到 Kotlin 时)
// Android 原生 OnClickListener
view.setOnClickListener({
  onClick(v: android.view.View) {
    console.log('被点击了')
  }
})

9.3 调用 Android 原生 API

这是 UTS 最强大的能力。通过直接 import Android SDK 的类,可以实现任何原生功能:

// uts-plugins/vibrate/index.uts
// 直接导入 Android 原生类
import Vibrator from 'android.os.Vibrator'
import VibrationEffect from 'android.os.VibrationEffect'
import Context from 'android.content.Context'
import Build from 'android.os.Build'

/**
 * 自定义震动(时长和强度)
 * @param duration 震动时长(毫秒)
 * @param amplitude 震动强度(1-255,仅 Android 8.0+ 支持)
 */
export function vibrate(duration: number, amplitude: number = 128): void {
  const context = UTSAndroid.getUniActivity()!
  const vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator

  if (Build.VERSION.SDK_INT >= 26) {
    // Android 8.0+ 支持设置震动强度
    const effect = VibrationEffect.createOneShot(duration, amplitude)
    vibrator.vibrate(effect)
  } else {
    // 旧版 API(deprecated)
    vibrator.vibrate(duration)
  }
}

/**
 * 检查设备是否有震动马达
 */
export function hasVibrator(): boolean {
  const context = UTSAndroid.getUniActivity()!
  const vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
  return vibrator.hasVibrator()
}

访问 Android Activity 和 Context

// UTSAndroid 是 uni-app x 提供的全局工具对象

// 获取 Activity(非空断言 ! 表示我们确定它不为 null)
const activity = UTSAndroid.getUniActivity()!  // MainActivity 实例
const appContext = UTSAndroid.getAppContext()!  // Application Context

// 在主线程执行(UI 操作必须在主线程)
UTSAndroid.runOnMainThread(() => {
  // 修改 UI 的代码
})

// 在子线程执行(网络请求、IO 操作)
UTSAndroid.runOnUIThread(() => {
  // 此处实际上会在后台线程运行
})

9.4 调用 iOS 原生 API

// uts-plugins/haptic/index.uts(iOS 触觉反馈)
import UIImpactFeedbackGenerator from 'UIKit'

type HapticStyle = 'light' | 'medium' | 'heavy'

export function hapticFeedback(style: HapticStyle = 'medium'): void {
  let feedbackStyle: UIImpactFeedbackGenerator.FeedbackStyle

  switch (style) {
    case 'light':
      feedbackStyle = .light
      break
    case 'heavy':
      feedbackStyle = .heavy
      break
    default:
      feedbackStyle = .medium
  }

  const generator = new UIImpactFeedbackGenerator(style: feedbackStyle)
  generator.prepare()
  generator.impactOccurred()
}

// iOS 剪贴板操作
import UIPasteboard from 'UIKit'

export function copyToClipboard(text: string): void {
  UIPasteboard.general.string = text
}

export function getClipboardContent(): string | null {
  return UIPasteboard.general.string
}

9.5 UTS 插件结构

UTS 插件是 uni-app x 的插件单元,遵循特定的目录结构:

uni_modules/
└── my-vibrate/                     # 插件名
    ├── package.json                # 插件配置
    ├── readme.md
    └── utssdk/
        ├── index.uts               # 所有平台通用逻辑(可选)
        ├── app-android/
        │   ├── index.uts           # Android 专属逻辑
        │   └── config.json         # Android 依赖配置
        └── app-ios/
            ├── index.uts           # iOS 专属逻辑
            └── config.json         # iOS 依赖配置
// app-android/config.json — Android 配置
{
  "minSdkVersion": 21,
  "dependencies": [
    // Gradle 依赖(如需要第三方 SDK)
    "com.google.android.gms:play-services-location:21.0.1"
  ],
  "abis": ["armeabi-v7a", "arm64-v8a"]
}

9.6 uvue 组件详解

uvue 是 uni-app x 的页面/组件文件格式,类似 .vue 但有一些重要差异:

<!-- pages/home/home.uvue -->
<template>
  <!-- 使用原生组件,不同于 .vue 文件 -->
  <list-view                        <!-- 高性能原生列表组件 -->
    :data-source="list"
    @load-more="loadMore"
  >
    <template #item="{ item }">
      <product-item :data="item" />
    </template>
  </list-view>
</template>

<script lang="uts">
// uvue 的 script 使用 UTS 语言
import { defineComponent, ref } from 'vue'

export default defineComponent({
  setup() {
    const list = ref<Product[]>([])

    async function loadMore() {
      // 这里可以直接调用 UTS 原生 API
      const data = await fetchProducts()
      list.value.push(...data)
    }

    return { list, loadMore }
  }
})
</script>

<style>
/* uvue 支持 rpx 单位(这是与 nvue 的重要区别)*/
.container {
  flex: 1;
  background-color: #f5f5f0;
  padding: 24rpx;
}
</style>

9.7 跨端能力封装模式

最佳实践:将平台相关的原生调用封装为跨端 API,让业务代码保持一致:

// uni_modules/my-haptic/utssdk/index.uts(跨平台通用接口)
export type HapticType = 'light' | 'medium' | 'heavy'

// #ifdef APP-ANDROID
import { vibrateAndroid } from './app-android/index.uts'
export function haptic(type: HapticType): void {
  vibrateAndroid(type)
}
// #endif

// #ifdef APP-IOS
import { hapticIOS } from './app-ios/index.uts'
export function haptic(type: HapticType): void {
  hapticIOS(type)
}
// #endif

// #ifndef APP
// H5 和小程序端 fallback
export function haptic(type: HapticType): void {
  uni.vibrateShort({ type })
}
// #endif

// 业务代码无需关心平台差异
import { haptic } from '@/uni_modules/my-haptic'
haptic('medium')  // 自动调用对应平台的实现

9.8 小结