在跨端开发中,UniApp 的核心优势在于“一次编写,多端运行”。但真正决定用户体验的,往往是那些需要调用原生能力的场景,比如扫码、定位和文件操作。这些能力在 App、小程序、H5 等平台上的实现差异巨大,如果直接调用各端原生 API,代码会迅速变得难以维护。
本文将围绕扫码、定位与文件系统三个高频功能,分享如何在 UniApp 中做一层优雅的跨端封装,让业务代码不再关心平台差异。
为什么需要跨端封装
UniApp 已经提供了 uni.scanCode、uni.getLocation、uni.getFileSystemManager 等统一 API,但实际开发中仍会遇到几个问题:
- 平台支持不一致:例如
uni.getFileSystemManager在 H5 端不可用,小程序与 App 的路径规则也不同。 - 权限处理差异:App 端需要动态申请权限,小程序端需要在
manifest.json中声明,H5 端则依赖浏览器授权。 - 返回值格式不统一:定位返回的坐标系、扫码返回的码类型,在不同端存在细微差别。
- 错误处理分散:用户拒绝授权、设备不支持、网络异常等场景,需要统一兜底。
因此,封装的目标不是替代 UniApp API,而是在其之上建立一层“业务友好”的适配层。
扫码能力的跨端封装
扫码在 App 和小程序中可以直接使用 uni.scanCode,H5 端则通常需要借助摄像头和第三方库(如 html5-qrcode)。封装时建议统一返回结构:
// utils/scan.js
export async function scanCode(options = {}) {
const { onlyFromCamera = false, scanType = ['barCode', 'qrCode'] } = options
// #ifdef H5
return scanByH5(options)
// #endif
// #ifndef H5
try {
const res = await uni.scanCode({ onlyFromCamera, scanType })
return {
success: true,
result: res.result,
scanType: res.scanType,
charSet: res.charSet
}
} catch (err) {
return { success: false, error: err.errMsg || '扫码失败' }
}
// #endif
}
对于 H5 端,可以动态加载 html5-qrcode,用 video 元素渲染扫码区域,识别成功后 resolve 相同结构。这样业务层只需判断 success 字段,无需关心当前平台。
需要注意两点:一是 App 端扫码页面是原生全屏,无法自定义 UI;二是小程序端 scanType 部分类型不支持,建议做降级处理。
定位能力的跨端封装
定位的复杂性主要来自坐标系和权限。UniApp 的 uni.getLocation 支持 type: 'wgs84' | 'gcj02',但各端默认值不同:
- App 端默认
wgs84,可指定gcj02 - 小程序端默认
wgs84,但微信小程序推荐gcj02 - H5 端依赖浏览器,通常返回
wgs84
封装时建议统一使用 gcj02(国内地图服务通用),并在 H5 端做坐标转换或直接提示用户。
// utils/location.js
export async function getLocation(options = {}) {
const { type = 'gcj02', geocode = false } = options
// #ifdef H5
if (!navigator.geolocation) {
return { success: false, error: '当前浏览器不支持定位' }
}
// #endif
try {
const res = await uni.getLocation({ type, geocode })
return {
success: true,
latitude: res.latitude,
longitude: res.longitude,
address: res.address || null,
raw: res
}
} catch (err) {
const msg = err.errMsg || ''
if (msg.includes('auth deny') || msg.includes('authorize')) {
return { success: false, error: '用户拒绝了定位权限', needAuth: true }
}
return { success: false, error: msg || '定位失败' }
}
}
在 App 端,还需要在 manifest.json 中配置定位模块,并在首次调用前动态申请权限。可以使用 plus.android.requestPermissions 或 UniApp 的 uni.authorize 做统一处理。
文件系统的跨端封装
文件系统是三者中平台差异最大的。小程序提供 uni.getFileSystemManager(),App 端有 plus.io,H5 端则只能用 Blob 和 FileReader。封装时应聚焦几个高频操作:保存文件、读取文件、删除文件、获取文件信息。
// utils/fs.js
export async function saveFile(tempFilePath, targetName) {
// #ifdef MP-WEIXIN
const fs = uni.getFileSystemManager()
const targetPath = `${wx.env.USER_DATA_PATH}/${targetName}`
return new Promise((resolve) => {
fs.saveFile({
tempFilePath,
filePath: targetPath,
success: (res) => resolve({ success: true, path: res.savedFilePath }),
fail: (err) => resolve({ success: false, error: err.errMsg })
})
})
// #endif
// #ifdef APP-PLUS
return new Promise((resolve) => {
plus.io.resolveLocalFileSystemURL(tempFilePath, (entry) => {
plus.io.resolveLocalFileSystemURL('_doc/', (root) => {
entry.copyTo(root, targetName, (newEntry) => {
resolve({ success: true, path: newEntry.fullPath })
}, (err) => resolve({ success: false, error: err.message }))
})
})
})
// #endif
// #ifdef H5
// H5 端通常直接使用 Blob 下载,不持久化
return { success: false, error: 'H5 端不支持持久化保存' }
// #endif
}
对于读取文件,小程序用 fs.readFile,App 用 plus.io.FileReader,H5 用 FileReader。建议统一返回 ArrayBuffer 或 base64,由业务层决定如何解析。
统一错误处理与权限管理
跨端封装最容易忽略的是错误码的统一。建议定义一套内部错误码:
PERMISSION_DENIED:用户拒绝授权NOT_SUPPORTED:当前平台不支持SYSTEM_ERROR:系统或网络异常USER_CANCEL:用户主动取消
在封装层捕获原生错误后,映射为上述错误码返回。业务层只需根据错误码决定是提示用户、跳转设置页,还是降级处理。
权限方面,可以封装一个 ensurePermission 方法,在调用扫码、定位前统一检查:
export async function ensurePermission(scope) {
// #ifdef APP-PLUS
// 使用 plus.android.requestPermissions
// #endif
// #ifdef MP-WEIXIN
const setting = await uni.getSetting()
if (!setting.authSetting[scope]) {
await uni.authorize({ scope })
}
// #endif
// #ifdef H5
// 浏览器权限在调用时自动触发
// #endif
}
小结
UniApp 的跨端能力已经覆盖了大部分场景,但扫码、定位、文件系统这三个领域由于平台底层差异大,仍然需要一层适配封装。封装的核心思路是:统一入参、统一返回结构、统一错误码、按平台条件编译。这样业务代码只需调用 scanCode()、getLocation()、saveFile(),而不必写满 #ifdef。
随着 UniApp 对鸿蒙、纯血鸿蒙等新平台的支持,这层封装的价值会进一步放大。建议将上述工具方法放在 utils/native/ 目录下,配合 TypeScript 类型定义,形成团队内部的跨端原生能力规范。
未经允许不得转载:任鹏个人博客 » UniApp 调用原生能力:扫码、定位与文件系统的跨端封装


朋友圈点赞图在线生成源码