Chapter 08

原生能力与插件

调用地理位置、相机、推送、分享等原生能力,掌握权限申请与插件市场使用

8.1 原生 API 体系

uni-app 封装了数百个设备原生 API,统一以 uni. 前缀调用。这些 API 在各平台有不同的底层实现,但开发者写一套代码即可适配:

能力类别主要 API
界面交互showToast、showModal、showLoading、showActionSheet
导航与路由navigateTo、setNavigationBarTitle、setTabBarBadge
媒体chooseImage、chooseVideo、previewImage、getImageInfo
文件uploadFile、downloadFile、saveFile、openDocument
位置getLocation、openLocation、chooseLocation
设备信息getSystemInfo、getWindowInfo、getNetworkType
传感器startAccelerometer、startCompass、onGyroscopeChange
支付requestPayment(微信支付、支付宝支付)
分享share(App 端)、onShareAppMessage(小程序端)
蓝牙/NFCopenBluetoothAdapter、startBluetoothDevicesDiscovery

8.2 地理位置

// 获取当前位置(需要权限)
async function getCurrentLocation() {
  try {
    // App 端需先检查并申请权限
    // #ifdef APP
    const authorized = await requestLocationPermission()
    if (!authorized) {
      uni.showToast({ title: '需要位置权限', icon: 'none' })
      return
    }
    // #endif

    const location = await uni.getLocation({
      type: 'gcj02',         // GCJ-02 坐标(国内地图标准)
      altitude: true,       // 高度信息(App 端有效)
      isHighAccuracy: true  // 高精度(GPS,耗电更多)
    })

    console.log('经度:', location.longitude)
    console.log('纬度:', location.latitude)
    console.log('精度(米):', location.accuracy)

    return location
  } catch (err) {
    console.error('获取位置失败:', err)
  }
}

// 在地图中打开位置
uni.openLocation({
  latitude: 39.9042,
  longitude: 116.4074,
  name: '天安门广场',
  address: '北京市东城区'
})

// 让用户在地图上选择位置
const chosen = await uni.chooseLocation({})
console.log('用户选择的地点:', chosen.name, chosen.latitude, chosen.longitude)
坐标系说明

中国大陆使用 GCJ-02(火星坐标系),GPS 和国际标准使用 WGS-84。高德地图、腾讯地图使用 GCJ-02;百度地图使用 BD-09。uni.getLocationtype 参数决定返回哪种坐标系,选错坐标系会导致位置偏移数百米。

8.3 相机与图片

// 选择/拍摄图片
async function selectImage() {
  const res = await uni.chooseImage({
    count: 9,                              // 最多选 9 张
    sizeType: ['original', 'compressed'], // 原图或压缩图
    sourceType: ['album', 'camera']       // 相册或相机
  })

  // 预览图片
  await uni.previewImage({
    urls: res.tempFilePaths,
    current: res.tempFilePaths[0]  // 从第一张开始预览
  })
}

// 压缩图片
async function compressImage(filePath: string) {
  const res = await uni.compressImage({
    src: filePath,
    quality: 80  // 压缩质量 0-100
  })
  return res.tempFilePath
}

// 拍视频
const video = await uni.chooseVideo({
  sourceType: ['album', 'camera'],
  maxDuration: 60,    // 最长 60 秒
  camera: 'back'       // 后置摄像头
})

8.4 权限申请最佳实践

App 端(Android/iOS)的原生权限需要运行时申请,如果直接调用 API 而不先申请权限,会导致 API 失败或 App 崩溃。

权限申请封装

// utils/permission.ts
// #ifdef APP-ANDROID
import { requestPermissions } from '@dcloudio/uni-app'
// #endif

/**
 * 申请定位权限(Android + iOS)
 * 返回 true 表示已授权
 */
export async function requestLocationPermission(): Promise<boolean> {
  // #ifdef APP-ANDROID
  const result = await uni.authorize({
    scope: 'scope.userLocation'
  }).catch(() => null)

  if (!result) {
    // 用户拒绝,引导去设置页开启
    const { confirm } = await uni.showModal({
      title: '需要位置权限',
      content: '请在设置中允许访问您的位置,以获得更好的服务体验。',
      confirmText: '去设置'
    })
    if (confirm) {
      uni.openAppAuthorizeSetting()
    }
    return false
  }
  return true
  // #endif

  // #ifdef APP-IOS
  // iOS 通过 Info.plist 中的描述文字申请,首次调用 API 时自动弹出系统弹窗
  return true
  // #endif

  // #ifndef APP
  return true  // H5 和小程序由浏览器/小程序框架处理
  // #endif
}

8.5 消息推送(uni-push 2.0)

uni-push 2.0 是 DCloud 推出的统一推送服务,底层集成了各厂商通道(FCM、华为、小米、OPPO、vivo、荣耀)和个推平台,实现系统级推送(App 关闭后仍能收到通知)。

客户端集成

// App.vue — 初始化推送
export default {
  onLaunch() {
    // #ifdef APP
    initPush()
    // #endif
  }
}

function initPush() {
  // 获取推送 token(cid),用于服务端向设备推送
  uni.getPushClientId({
    success(res) {
      const cid = res.cid
      console.log('推送 ClientID:', cid)
      // 将 cid 发送到服务端,绑定用户
      http.post('/api/push/register', { cid })
    }
  })

  // 监听推送消息(App 处于前台时)
  uni.onPushMessage((res) => {
    const { data } = res
    console.log('收到推送:', data)

    if (data.type === 'order') {
      // 订单状态推送,跳转到订单页
      uni.navigateTo({ url: `/pages/order/detail?id=${data.orderId}` })
    }
  })
}

服务端发送推送(云函数)

// cloudfunctions/sendPush/index.js
const uniPush = uniCloud.getPushManager({ dcloudAppid: '__UNI__XXXXXXX' })

exports.main = async (event) => {
  const { cid, title, content, data } = event

  await uniPush.sendMessage({
    push_clientid: cid,     // 目标设备 ClientID
    title,                  // 通知栏标题
    content,                // 通知栏内容
    payload: data,          // 自定义数据(JSON)
    badge: 1,              // iOS App 图标角标
    sound: 'default'      // 通知声音
  })

  return { code: 0 }
}

8.6 uni 插件市场

uni 插件市场(ext.dcloud.net.cn)是 uni-app 的官方插件生态,拥有数万个插件,覆盖 UI 组件、业务功能、设备能力等。

安装插件的方式

  1. HBuilderX 一键导入:在插件市场页面点击"使用 HBuilderX 导入插件",自动安装到 uni_modules/ 目录
  2. npm 安装:部分插件支持 npm 安装(如 uni-ui)
  3. 手动复制:将插件文件复制到项目目录

最常用的插件

uni-ui
官方推出的多端 UI 组件库,包含 30+ 组件,兼容所有平台。npm install @dcloudio/uni-ui
uview-plus(u-view)
社区最流行的 UI 库,组件丰富,文档详尽,支持 Vue 3 + TypeScript。
wxml-to-canvas
在 Canvas 上渲染 WXML 结构,用于生成海报/分享图(微信小程序专用)。
z-paging
强大的分页/下拉刷新/加载更多组件,支持虚拟列表,大幅简化列表页开发。
uni-id
官方用户体系,提供注册、登录(短信验证码、微信一键登录等)、鉴权等完整方案。

8.7 分享功能

// 微信小程序:配置页面分享
import { onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app'

// 分享给朋友
onShareAppMessage(() => ({
  title: '这个商品超好用!',
  path: `/pages/detail/detail?id=${productId.value}`,
  imageUrl: 'https://example.com/share-cover.jpg'
}))

// 分享到朋友圈(需要在 pages.json 中配置 enableShareTimeline)
onShareTimeline(() => ({
  title: '发现好物,推荐给你!',
  query: `id=${productId.value}`
}))

// App 端:系统原生分享
// #ifdef APP
uni.share({
  provider: 'weixin',     // 分享平台:weixin/qq/sinaweibo
  scene: 'WXSceneSession', // WXSceneSession(发给朋友)/ WXSenceTimeline(朋友圈)
  type: 0,               // 0=网页链接 1=纯文字 2=图片 3=视频
  href: 'https://example.com/product/42',
  title: '这个商品超好用!',
  summary: '限时特惠,点击查看详情',
  imageUrl: 'https://example.com/cover.jpg'
})
// #endif

8.8 振动与音频

// 震动(提供轻微的触觉反馈)
uni.vibrateShort({ type: 'light' })  // light/medium/heavy(iOS 支持)
uni.vibrateLong()  // 长震动(400ms)

// 背景音乐
const bgm = uni.getBackgroundAudioManager()
bgm.title = '轻音乐'
bgm.src = 'https://example.com/music.mp3'
bgm.play()  // App 后台也能继续播放

// 音效(短音频)
const audio = uni.createInnerAudioContext()
audio.src = '/static/sounds/click.mp3'
audio.play()

8.9 小结