qsh-webview-sdk
v2.4.8
Published
UniApp WebView SDK for WeChat MiniProgram and App
Maintainers
Readme
QSH WebView SDK
一个重构后的 QSH WebView SDK,支持微信小程序和 APP 端的 WebView 与原生端通信。
特性
- ✅ 支持微信小程序 WebView
- ✅ 支持 APP 端 (Plus/NVUE/UVUE)
- ✅ 模块化架构,易于维护
- ✅ TypeScript 类型支持
- ✅ UMD 和 ESM 双格式输出
- ✅ 自动环境检测
安装
NPM 安装
npm install qsh-webview-sdkCDN 引入
<script src="path/to/qsh-webview-sdk.umd.js"></script>使用方法
在 HTML 中引入
<!-- 引入 QSH WebView SDK(内部会在微信内的小程序 web-view 环境自动注入 jweixin) -->
<script type="text/javascript" src="path/to/qsh-webview-sdk.umd.js"></script>说明:通过 UMD 方式引入后,默认在全局暴露
window.qsh。
NPM 安装与使用(ESM)
npm install qsh-webview-sdkimport qsh from 'qsh-webview-sdk';
// 等待 SDK 就绪后再调用 API(内部也有自动等待机制)
qsh.ready().then(() => {
});微信 clientId 注入
import qsh from 'qsh-webview-sdk';
// 如果路由使用了hash模式,则只需在App.vue中config一次,url可以不传
// 如果是非hash模式, 则需要在每次路由变化时重新config, 且要传入完整的url
if(qsh.environment.isWeixinMiniProgram) {
qsh.config({
clientId: 'your-client-id', // 注意区分生产和测试的clientId
isProd: false,
debug: false, // isProd 为 true 时会强制关闭
url: '' // 可选:传入后用于微信签名,默认使用当前页面 URL
})
}
// isProd 默认为 false: 根据环境传入
// debug 默认为 false: 仅在 isProd 为 false 时可开启
// url 默认为空: 有值时会替代当前页面 URL 参与微信签名
// 依赖微信 JS-SDK 的能力前,建议先注入 clientId
// 已调用 qsh.config() 时,qsh.ready() 会在微信小程序 web-view 中额外等待 wx.config 完成
await qsh.ready();初始化
- 浏览器环境下,SDK 会在 DOM 就绪后自动初始化,并触发
UniAppJSBridgeReady事件。 - SSR/Node 环境不会执行自动初始化;在客户端 Hydration 后会按上述规则自动初始化。
- 所有对外 API(如
postMessage、getEnv等)内置自动等待机制:即使在未就绪时调用,也会在就绪后自动执行,无需手动监听事件。 - 仍可使用
qsh.ready()等待准备完成;如需“控制初始化时机”,可手动调用qsh.init()。该方法是幂等的,多次调用仅首次生效。
注:当检测到运行于微信内且为小程序 web-view 场景时,SDK 将自动按需注入
jweixin-1.6.2.js(若页面已引入则不会重复注入)。若你需要自定义版本或自行控制加载时机,可在页面提前引入 jweixin,SDK 将跳过自动注入。
基础使用
// 直接调用,无需等待(内部会自动等待就绪)
qsh.postMessage({ data: {} });
// 如需在 UI 上显示“就绪状态”,可以使用 ready()
qsh.ready().then(() => {
console.log('SDK 已准备就绪');
});消息通信
// 发送消息到原生端
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 }
});环境检测
// 获取当前环境信息
const env = qsh.environment;
console.log('环境类型:', env.type); // 'weixin' | 'offline' | 'uvue' | 'webview'
console.log('是否微信小程序:', env.isWeixinMiniProgram);
console.log('是否App:', env.isUniApp);
console.log('是否鸿蒙:', env.isUvue);API 文档
消息 API
| 方法 | 说明 | 参数 |
|------|------|------|
| postMessage | 向原生端发送消息 | { data?: any } |
| getEnv | 获取当前环境信息 | callback: Function |
图片 API
| 方法 | 说明 | 参数 |
|------|------|------|
| chooseImage | 选择图片(自动环境适配) | { count?: number; sizeType?: string[]; sourceType?: string[]; success?: Function; fail?: Function; complete?: Function } |
| chooseImageAsync | 选择图片(Promise 版) | { count?: number; sizeType?: string[]; sourceType?: string[] } |
扫码 API
| 方法 | 说明 | 参数 |
|------|------|------|
| scanCode | 扫码(自动环境适配) | { onlyFromCamera?: boolean; scanType?: string[]; success?: Function; fail?: Function; complete?: Function } |
| scanCodeAsync | 扫码(Promise 版) | { onlyFromCamera?: boolean; scanType?: string[] } |
位置 API
| 方法 | 说明 | 参数 |
|------|------|------|
| getLocation | 获取当前位置 | { type?: string; altitude?: boolean; success?: Function; fail?: Function } |
| getLocationAsync | 获取位置(Promise 版) | { type?: string; altitude?: boolean } |
| openLocation | 查看位置(打开地图) | { latitude: number; longitude: number; type?: 'wgs84' \| 'gcj02'; name?: string; address?: string; scale?: number; success?: Function; fail?: Function } |
| openLocationAsync | 查看位置(Promise 版) | { latitude: number; longitude: number; type?: 'wgs84' \| 'gcj02'; 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。
- 自动按需加载并配置 jweixin,等待
- UniApp 侧(APP/NVUE/UVUE/H5+)
- WebView 通过桥接向宿主发送
chooseImage指令:内部使用callApiInWebView('chooseImage', params)。 - 宿主侧调用
uni.chooseImage并将结果回传给 H5 页面;结果包含res.tempFilePaths、res.tempFiles。
- WebView 通过桥接向宿主发送
- 调用时机与等待
- 所有 API 内置
qsh.ready()等待机制,即使在未就绪时调用也会在 Bridge 就绪后自动执行。 - 微信侧还会在配置完成后再调用;配置失败将进入
fail回调。
- 所有 API 内置
返回结果差异
- 微信:
res.localIds: string[] - UniApp:
res.tempFilePaths: string[],res.tempFiles: Array<{ path: string, size: number }>
使用示例
- 回调版
qsh.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success(res) {
// 微信:res.localIds
// UniApp:res.tempFilePaths / res.tempFiles
},
fail(err) {
console.error(err);
}
});- Promise 版
try {
const res = await qsh.chooseImageAsync({ count: 1 });
// 处理 res
} catch (e) {
console.error(e);
}- 常量辅助
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 |
开发
安装依赖
npm install开发模式
npm run dev构建
npm run build本地调试
Vue3 演示系统(推荐)
使用完整的 Vue3 演示系统进行调试和测试:
npm run play
# 或
npm run play:vue3访问:http://localhost:5173
功能:
- 11 个功能演示页面
- 完整的交互界面
- 实时日志记录
- 移动端适配
说明:vue3-playground/vite.config.js 已将包名 qsh-webview-sdk 定位到仓库 src/index.js,用于直接联调当前源码。
其他命令
# 生产构建
npm run build:vue3
# 预览构建
npm run preview: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
│ └── 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)
以下为近期需要支持的功能清单,按平台划分(未勾选表示待实现)。
小程序(web-view)
- [] 扫码 ✅
- [] 获取位置 ✅
- [] 查看位置 ✅
- [] 微信分享 ✅
- [] 照片选择 ✅
- [] 打开地图选择位置 ✅
- [] 人脸核身 ✅
APP(UniApp/Plus/NVUE/UVUE)
- [] 扫码 ✅
- [] 获取位置 ✅
- [] 查看位置 ✅
- [] 照片选择 ✅
- [] 持续定位 ✅
- [x] 相机
- [x] 蓝牙
- [] 微信分享 ✅
- [] 照片选择 ✅
- [x] 文件选择
- [] 打开地图选择位置 ✅
- [] 人脸核身 ✅
- [] 打印服务 ✅
扫码功能
基础使用
// 回调方式
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);
}参数说明
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
位置功能
获取当前位置
// 回调方式
qsh.getLocation({
type: 'wgs84', // 坐标类型:wgs84(GPS)
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: 'wgs84',
altitude: true
});
console.log('位置:', location.latitude, location.longitude);
} catch (error) {
console.error('定位失败:', error);
}查看位置(打开地图)
// 打开地图查看指定位置
// type 默认 wgs84;小程序会转成 gcj02 后调用 wx.openLocation,App 会直接使用 wgs84 坐标
qsh.openLocation({
latitude: 39.908823,
longitude: 116.397470,
type: 'wgs84',
name: '天安门',
address: '北京市东城区东长安街',
scale: 15, // 缩放级别 1-28
success: () => {
console.log('地图已打开');
}
});
// Promise 版本
await qsh.openLocationAsync({
latitude: 39.908823,
longitude: 116.397470,
type: 'wgs84',
name: '天安门'
});坐标类型
目前定位相关接口 App 端建议统一使用 wgs84 坐标。openLocation 支持 wgs84 / gcj02,SDK 会按环境自动转换坐标。
| 类型 | 说明 | 常量 |
|------|------|------|
| 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)
// 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('定位服务已停止')
});蓝牙功能(仅 APP)
// 初始化蓝牙
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)
});地图选择位置(仅 APP)
// 打开地图选择位置(APP)
qsh.chooseLocation({
// 初始经纬度 不传默认取当前定位
latitude: 39.908823,
longitude: 116.397470,
success: (res) => {
console.log(res.location);
console.log(res.formatted_address);
console.log(res.addressComponent);
},
fail: (err) => console.error(err)
});人脸核身
// 人脸核身:跳转宿主小程序 /pages/face/index,并将参数拼到 query 上
qsh.faceVerify({
name: '姓名',
idCardNumber: '身份证号'
});打印服务(仅 APP)
// 调用打印服务
qsh.printPdf({
url: '', // pdf文件地址
success: () => console.log('打印成功'),
fail: (err) => console.error('打印失败:', err)
});文件预览
qsh.filePreview({
url: 'https://example.com/files/demo.pdf', // 文件地址
fileType: 'pdf', // 文件类型
showMenu: true, // 是否显示右上角菜单,默认 false
success: (res) => console.log('文件预览请求成功', res),
fail: (err) => console.error('文件预览失败:', err)
});
// Promise 用法
await qsh.filePreviewAsync({
url: 'https://example.com/files/demo.pdf',
fileType: 'pdf',
showMenu: false
});
fileType仅支持以下取值:doc、xls、ppt、docx、xlsx、pptx。 微信小程序web-view下,SDK 会通过wx.miniProgram.postMessage把预览请求投递给宿主,再调用wx.miniProgram.navigateBack返回基座;APP 端通过callApiInWebView调用基座能力。
跳转其他微信小程序
qsh.navigateToMiniProgram({
appid: '',
path: '',
env: 'release', // release/trial/develop
success: (res) => console.log('跳转成功', res),
fail: (err) => console.error('跳转失败:', err),
});
appid在微信小程序环境下传目标小程序AppID,在 App-plus 环境下传微信小程序原始 ID。env为可选参数,取值release/trial/develop,不传时由基座按默认逻辑处理。 微信小程序web-view下,SDK 会先通过wx.miniProgram.postMessage把跳转请求投递给宿主,再调用wx.miniProgram.navigateBack触发宿主处理,因此当前 H5 页面会退出,且只能拿到“请求已投递”的结果,不能拿到宿主侧实时回调。
在小程序中通过弹出层分享(仅小程序)
qsh.manualShare({
title: '分享标题',
h5Url: 'https://example.com/activity?id=123', // 要通过分享打开的h5页面地址
clientId: 'your-client-id', // 注意区分生产和测试的clientId
isPublic: false, // 默认false 分享打开的h5页面如果不需要登录权限可直接打开则传true
imageUrl: 'https://example.com/share.png',
description: '分享描述',
success: (res) => console.log('分享成功', res),
fail: (err) => console.error('分享失败:', err)
});对于isPublic:true时 用户点击分享的内容后会直接从基座首页跳转到h5Url, 对于isPublic:false时,基座完成登录流程后会携带参数h5Url=${encodeURIComponent(h5Url)}跳转到业务配置的h5首页, 在h5首页获取code和h5Url后根据需要自行处理登录逻辑和跳转 此方法会销毁当前H5页面并返回小程序基座
兼容性
- 微信小程序 WebView
- UniApp APP 端 (Android/iOS)
- H5+ 环境
- NVUE 页面
- UVUE 页面 (UniApp X)
License
MIT
CommonJS/Require 使用
// 安装:npm install qsh-webview-sdk
const qsh = require('qsh-webview-sdk');
qsh.ready().then(() => {
qsh.postMessage({ data: { action: 'init' } });
});在框架中使用
以下示例均利用 qsh.ready() 确保就绪;注意 SDK 自带自动等待,直接调用 API 也可正常执行。
- Vue 3
<script setup>
import { onMounted } from 'vue';
import qsh from 'qsh-webview-sdk';
onMounted(async () => {
// 小程序环境下要先进行签名
if (qsh.environment.isWeixinMiniProgram) {
qsh.config({
clientId: '', // 注意区分生产和测试的clientId
isProd: import.meta.env.MODE === 'production',
debug: false,
url: ''
})
}
qsh.ready().catch((err) => {
console.error('qsh ready failed', err)
})
});
</script>- React
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;
}