UniApp WebView SDK for WeChat MiniProgram and App
npm install qsh-webview-sdk一个重构后的 QSH WebView SDK,支持微信小程序和 APP 端的 WebView 与原生端通信。
- ✅ 支持微信小程序 WebView
- ✅ 支持 APP 端 (Plus/NVUE/UVUE)
- ✅ 模块化架构,易于维护
- ✅ TypeScript 类型支持
- ✅ UMD 和 ESM 双格式输出
- ✅ 自动环境检测
``bash`
npm install qsh-webview-sdk
`html`
`html`
> 说明:通过 UMD 方式引入后,默认在全局暴露 window.qsh。
`bash`
npm install qsh-webview-sdk
`javascript
import qsh from 'qsh-webview-sdk';
// 等待 SDK 就绪后再调用 API(内部也有自动等待机制)
qsh.ready().then(() => {
qsh.navigateTo({ url: '/pages/test/test' });
});
`
- 浏览器环境下,SDK 会在 DOM 就绪后自动初始化,并触发 UniAppJSBridgeReady 事件。navigateTo
- SSR/Node 环境不会执行自动初始化;在客户端 Hydration 后会按上述规则自动初始化。
- 所有对外 API(如 、postMessage、getEnv 等)内置自动等待机制:即使在未就绪时调用,也会在就绪后自动执行,无需手动监听事件。qsh.ready()
- 仍可使用 等待准备完成;如需“控制初始化时机”,可手动调用 qsh.init()。该方法是幂等的,多次调用仅首次生效。
> 注:当检测到运行于微信内且为小程序 web-view 场景时,SDK 将自动按需注入 jweixin-1.6.2.js(若页面已引入则不会重复注入)。若你需要自定义版本或自行控制加载时机,可在页面提前引入 jweixin,SDK 将跳过自动注入。
`javascript
// 直接调用,无需等待(内部会自动等待就绪)
qsh.navigateTo({ url: '/pages/detail/detail?id=1' });
// 如需在 UI 上显示“就绪状态”,可以使用 ready()
qsh.ready().then(() => {
console.log('SDK 已准备就绪');
});
`
`javascript
// 跳转到新页面
qsh.navigateTo({
url: '/pages/detail/detail?id=1'
});
// 返回上一页
qsh.navigateBack({
delta: 1
});
// 切换 Tab
qsh.switchTab({
url: '/pages/home/home'
});
// 重启应用
qsh.reLaunch({
url: '/pages/index/index'
});
// 重定向
qsh.redirectTo({
url: '/pages/login/login'
});
`
`javascript
// 发送消息到原生端
qsh.postMessage({
data: {
action: 'share',
title: '分享标题',
content: '分享内容'
}
});
// 获取环境信息
qsh.getEnv(function(result) {
console.log('环境信息:', result);
// result: { miniprogram: true, weixin: true }
// 或 { app: true, plus: true } 或 { h5: true }
});
`
`javascript
// 获取当前环境信息
const env = qsh.environment;
console.log('环境类型:', env.type); // 'weixin' | 'plus' | 'nvue' | 'uvue' | 'UniApp' | 'h5'
console.log('是否微信小程序:', env.isWeixinMiniProgram);
console.log('是否 APP:', env.isAppPlus);
`
| 方法 | 说明 | 参数 |
|------|------|------|
| navigateTo | 保留当前页面,跳转到应用内的某个页面 | { url: string } |navigateBack
| | 关闭当前页面,返回上一页面或多级页面 | { delta?: number } |switchTab
| | 跳转到 tabBar 页面 | { url: string } |reLaunch
| | 关闭所有页面,打开到应用内的某个页面 | { url: string } |redirectTo
| | 关闭当前页面,跳转到应用内的某个页面 | { url: string } |
| 方法 | 说明 | 参数 |
|------|------|------|
| postMessage | 向原生端发送消息 | { data?: any } |getEnv
| | 获取当前环境信息 | callback: Function |
| 方法 | 说明 | 参数 |
|------|------|------|
| chooseImage | 选择图片(自动环境适配) | { count?: number; sizeType?: string[]; sourceType?: string[]; success?: Function; fail?: Function; complete?: Function } |chooseImageAsync
| | 选择图片(Promise 版) | { count?: number; sizeType?: string[]; sourceType?: string[] } |
| 方法 | 说明 | 参数 |
|------|------|------|
| scanCode | 扫码(自动环境适配) | { onlyFromCamera?: boolean; scanType?: string[]; success?: Function; fail?: Function; complete?: Function } |scanCodeAsync
| | 扫码(Promise 版) | { onlyFromCamera?: boolean; scanType?: string[] } |
| 方法 | 说明 | 参数 |
|------|------|------|
| getLocation | 获取当前位置 | { type?: string; altitude?: boolean; success?: Function; fail?: Function } |getLocationAsync
| | 获取位置(Promise 版) | { type?: string; altitude?: boolean } |openLocation
| | 查看位置(打开地图) | { latitude: number; longitude: number; name?: string; address?: string; scale?: number; success?: Function; fail?: Function } |openLocationAsync
| | 查看位置(Promise 版) | { latitude: number; longitude: number; name?: string; address?: string; scale?: number } |startLocationUpdate
| | 开启前台持续定位(仅 APP) | { type?: string; needFullAccuracy?: boolean; success?: Function; fail?: Function } |stopLocationUpdate
| | 停止前台持续定位(仅 APP) | { success?: Function; fail?: Function } |onLocationChange
| | 监听实时位置变化(仅 APP) | callback: Function |offLocationChange
| | 取消监听位置变化(仅 APP) | callback?: Function |onLocationChangeError
| | 监听位置更新错误(仅 APP) | callback: Function |offLocationChangeError
| | 取消监听位置更新错误(仅 APP) | callback?: Function |
#### 实现原理
- 微信小程序侧
- 自动按需加载并配置 jweixin,等待 qsh.weixin.isConfigReady() 为 true 后,SDK 内部调用 wx.chooseImage(options);开发者统一调用 qsh.chooseImage(options)。count
- 透传 /sizeType/sourceType,回调结果遵循微信规范:res.localIds。chooseImage
- UniApp 侧(APP/NVUE/UVUE/H5+)
- WebView 通过桥接向宿主发送 指令:内部使用 callApiInWebView('chooseImage', params)。uni.chooseImage
- 宿主侧调用 并将结果回传给 H5 页面;结果包含 res.tempFilePaths、res.tempFiles。qsh.ready()
- 调用时机与等待
- 所有 API 内置 等待机制,即使在未就绪时调用也会在 Bridge 就绪后自动执行。fail
- 微信侧还会在配置完成后再调用;配置失败将进入 回调。
#### 返回结果差异
- 微信:res.localIds: string[]res.tempFilePaths: string[]
- UniApp:, res.tempFiles: Array<{ path: string, size: number }>
#### 使用示例
1) 回调版
`javascript`
qsh.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success(res) {
// 微信:res.localIds
// UniApp:res.tempFilePaths / res.tempFiles
},
fail(err) {
console.error(err);
}
});
2) Promise 版
`javascript`
try {
const res = await qsh.chooseImageAsync({ count: 1 });
// 处理 res
} catch (e) {
console.error(e);
}
3) 常量辅助
`javascript`
qsh.chooseImage({
sizeType: [qsh.ImageSizeTypes.COMPRESSED],
sourceType: [qsh.ImageSourceTypes.ALBUM]
});
| 属性 | 类型 | 说明 |
|------|------|------|
| environment.type | string | 环境类型 |environment.isWeixinMiniProgram
| | boolean | 是否微信小程序 |environment.isAppPlus
| | boolean | 是否 APP Plus |environment.isNvue
| | boolean | 是否 NVUE |environment.isUvue
| | boolean | 是否 UVUE |environment.isUniApp
| | boolean | 是否 UniApp |environment.isHtml5Plus
| | boolean | 是否 Html5Plus |
`bash`
npm install
`bash`
npm run dev
`bash`
npm run build
使用完整的 Vue3 演示系统进行调试和测试:
`bash`
npm run play或
npm run play:vue3
访问:http://localhost:5173
功能:
- 11 个功能演示页面
- 完整的交互界面
- 实时日志记录
- 移动端适配
说明:vue3-playground/vite.config.js 已将包名 qsh-webview-sdk 定位到仓库 src/index.js,用于直接联调当前源码。
`bash生产构建
npm run build:vue3
项目结构
`
src/
├── core/ # 核心模块
│ ├── environment.js # 环境检测
│ ├── bridge.js # 桥接初始化
│ ├── messenger.js # 消息通信
│ ├── plugin-manager.js # 插件管理器
│ ├── interceptor.js # 拦截器链
│ ├── state-store.js # 状态仓库
│ ├── error-codes.js # 错误码定义
│ ├── error-normalizer.js # 错误标准化
│ └── ...
├── platforms/ # 平台适配
│ ├── weixin.js # 微信小程序
│ └── app.js # APP 端
├── api/ # API 接口
│ ├── image.js # 图片 API
│ ├── scan.js # 扫码 API
│ ├── location.js # 位置 API
│ ├── navigation.js # 导航 API
│ └── message.js # 消息 API
├── index.js # 主入口
└── index.d.ts # 类型定义vue3-playground/ # Vue3 演示系统(功能完整)
├── src/
│ ├── router/ # 路由配置
│ ├── views/ # 11 个演示页面
│ ├── components/ # 公共组件
│ └── composables/ # 组合式函数
└── ...
uni-playground/ # UniApp 宿主端(用于真机测试)
references/ # 开发参考(如原版 uni.webview.1.5.6.js)
`开发计划(Roadmap)
以下为近期需要支持的功能清单,按平台划分(未勾选表示待实现)。
$3
- [x] 扫码 ✅
- [x] 获取位置 ✅
- [x] 查看位置 ✅
- [x] 照片选择 ✅
- [ ] 相机
- [ ] 打开地图选择位置
- [ ] 人脸核身
$3
- [x] 扫码 ✅
- [x] 获取位置 ✅
- [x] 查看位置 ✅
- [x] 照片选择 ✅
- [x] 持续定位 ✅
- [ ] 相机
- [ ] 蓝牙
- [ ] WiFi
- [ ] 微信分享
- [x] 照片选择 ✅
- [ ] 文件选择
- [ ] 打开地图选择位置
- [ ] 人脸核身
- [ ] 打印服务
$3
#### 基础使用
`javascript
// 回调方式
qsh.scanCode({
success: (res) => {
console.log('扫码结果:', res.result);
console.log('扫码类型:', res.scanType);
},
fail: (err) => {
if (err.code === qsh.errors.codes.SCAN_CANCELLED) {
console.log('用户取消扫码');
} else {
console.error('扫码失败:', err);
}
}
});// Promise 版本
try {
const result = await qsh.scanCodeAsync({
scanType: ['qrCode']
});
console.log('扫码结果:', result.result);
} catch (error) {
console.error('扫码失败:', error);
}
`#### 参数说明
`javascript
qsh.scanCode({
onlyFromCamera: true, // 只从相机扫码(默认 true)
scanType: ['qrCode', 'barCode'], // 扫码类型
success: (res) => {
// res.result - 扫码内容
// res.scanType - 扫码类型
// res.charSet - 字符集(仅 APP/微信)
}
});
`#### 扫码类型
| 类型 | 说明 | 常量 |
|------|------|------|
|
qrCode | 二维码 | qsh.ScanTypes.QR_CODE |
| barCode | 一维码(条形码) | qsh.ScanTypes.BAR_CODE |
| datamatrix | Data Matrix 码 | qsh.ScanTypes.DATA_MATRIX |
| pdf417 | PDF417 条码 | qsh.ScanTypes.PDF417 |#### 平台差异
- 微信小程序:调用
wx.scanQRCode,自动配置
- APP 端:调用 uni.scanCode,通过 WebView 桥接
- 返回格式:两端已统一为 { result, scanType, charSet }参考文档:UniApp scanCode API
$3
#### 获取当前位置
`javascript
// 回调方式
qsh.getLocation({
type: 'wgs84', // 坐标类型:wgs84(GPS), gcj02(国测局), bd09(百度)
altitude: false, // 是否返回高度信息
success: (res) => {
console.log('纬度:', res.latitude);
console.log('经度:', res.longitude);
console.log('精度:', res.accuracy);
console.log('速度:', res.speed);
},
fail: (error) => {
if (error.code === qsh.errors.codes.LOCATION_NO_PERMISSION) {
console.log('无定位权限');
}
}
});// Promise 版本
try {
const location = await qsh.getLocationAsync({
type: 'gcj02',
altitude: true
});
console.log('位置:', location.latitude, location.longitude);
} catch (error) {
console.error('定位失败:', error);
}
`#### 查看位置(打开地图)
`javascript
// 打开地图查看指定位置
qsh.openLocation({
latitude: 39.908823,
longitude: 116.397470,
name: '天安门',
address: '北京市东城区东长安街',
scale: 15, // 缩放级别 1-28
success: () => {
console.log('地图已打开');
}
});// Promise 版本
await qsh.openLocationAsync({
latitude: 39.908823,
longitude: 116.397470,
name: '天安门'
});
`#### 坐标类型
| 类型 | 说明 | 常量 |
|------|------|------|
|
wgs84 | GPS 坐标 | qsh.CoordinateTypes.WGS84 |
| gcj02 | 国测局坐标(火星坐标) | qsh.CoordinateTypes.GCJ02 |
| bd09 | 百度坐标 | qsh.CoordinateTypes.BD09 |#### 平台差异
- 微信小程序:调用
wx.getLocation / wx.openLocation
- APP 端:调用 uni.getLocation / uni.openLocation(通过 WebView 桥接)
- 返回格式:已统一参考文档:UniApp getLocation
#### 持续定位(仅 APP)
`javascript
// 1. 开启前台持续定位服务
qsh.startLocationUpdate({
type: 'wgs84', // 坐标类型
needFullAccuracy: true, // 是否需要高精度
success: () => console.log('定位服务已开启'),
fail: (err) => console.error('开启失败:', err)
});// 2. 监听位置变化
qsh.onLocationChange((res) => {
console.log('位置:', res.latitude, res.longitude);
console.log('精度:', res.accuracy, '米');
});
// 3. 监听定位错误(可选)
qsh.onLocationChangeError((err) => {
console.error('定位出错:', err);
});
// 4. 停止监听
qsh.offLocationChange();
qsh.offLocationChangeError();
// 5. 停止定位服务
qsh.stopLocationUpdate({
success: () => console.log('定位服务已停止')
});
`$3
`javascript
// 相机拍照(小程序/APP)
qsh.takePhoto({
quality: 'high', // 图片质量:low, normal, high
sizeType: 'compressed', // 图片尺寸:original, compressed
success: (res) => {
console.log('照片路径:', res.tempImagePath);
},
fail: (err) => console.error(err)
});
`$3
`javascript
// 初始化蓝牙
qsh.openBluetoothAdapter({
success: () => console.log('蓝牙初始化成功'),
fail: (err) => console.error('蓝牙初始化失败:', err)
});// 开始搜索设备
qsh.startBluetoothDevicesDiscovery({
services: ['0000FFE0-0000-1000-8000-00805F9B34FB'],
success: () => console.log('开始搜索设备'),
fail: (err) => console.error('搜索失败:', err)
});
// 监听设备发现
qsh.onBluetoothDeviceFound((devices) => {
console.log('发现设备:', devices);
});
// 连接设备
qsh.createBLEConnection({
deviceId: 'device-id',
success: () => console.log('连接成功'),
fail: (err) => console.error('连接失败:', err)
});
`$3
`javascript
// 获取 WiFi 信息
qsh.getWifiInfo({
success: (res) => {
console.log('WiFi SSID:', res.wifi.SSID);
console.log('WiFi BSSID:', res.wifi.BSSID);
},
fail: (err) => console.error(err)
});// 连接 WiFi
qsh.connectWifi({
SSID: 'wifi-name',
password: 'wifi-password',
success: () => console.log('连接成功'),
fail: (err) => console.error('连接失败:', err)
});
`$3
`javascript
// 分享到微信
qsh.shareToWeixin({
type: 'webpage', // 分享类型:text, image, webpage, music, video
title: '分享标题',
description: '分享描述',
webpageUrl: 'https://example.com',
imageUrl: 'https://example.com/image.jpg',
success: () => console.log('分享成功'),
fail: (err) => console.error('分享失败:', err)
});
`$3
`javascript
// 选择文件
qsh.chooseFile({
type: 'all', // 文件类型:all, image, video, audio, file
count: 1,
success: (res) => {
console.log('文件路径:', res.tempFilePaths);
console.log('文件信息:', res.tempFiles);
},
fail: (err) => console.error(err)
});
`$3
`javascript
// 打开地图选择位置(小程序/APP)
qsh.chooseLocation({
latitude: 39.908823, // 初始纬度
longitude: 116.397470, // 初始经度
success: (res) => {
console.log('选择位置:', res.name);
console.log('地址:', res.address);
console.log('纬度:', res.latitude);
console.log('经度:', res.longitude);
},
fail: (err) => console.error(err)
});
`$3
`javascript
// 人脸核身(小程序/APP)
qsh.faceVerify({
verifyType: 'idcard', // 核身类型:idcard, bankcard, driver
idCard: '身份证号',
name: '姓名',
success: (res) => {
console.log('核身结果:', res.verifyResult);
console.log('核身分数:', res.score);
},
fail: (err) => console.error(err)
});
`$3
`javascript
// 打印文本
qsh.printText({
content: '打印内容',
printerName: '打印机名称',
success: () => console.log('打印成功'),
fail: (err) => console.error('打印失败:', err)
});// 打印图片
qsh.printImage({
imagePath: '/path/to/image.jpg',
printerName: '打印机名称',
success: () => console.log('打印成功'),
fail: (err) => console.error('打印失败:', err)
});
`兼容性
- 微信小程序 WebView
- UniApp APP 端 (Android/iOS)
- H5+ 环境
- NVUE 页面
- UVUE 页面 (UniApp X)
License
MIT
$3
`javascript
// 安装:npm install qsh-webview-sdk
const qsh = require('qsh-webview-sdk');qsh.ready().then(() => {
qsh.postMessage({ data: { action: 'init' } });
});
`$3
以下示例均利用
qsh.ready() 确保就绪;注意 SDK 自带自动等待,直接调用 API 也可正常执行。1) Vue 3
`javascript
`2) React
`javascript
import { useEffect } from 'react';
import qsh from 'qsh-webview-sdk';export default function App() {
useEffect(() => {
let cancelled = false;
qsh.ready().then(() => {
if (!cancelled) {
qsh.postMessage({ data: { action: 'mounted' } });
}
});
return () => { cancelled = true; };
}, []);
return null;
}
``