uni-app + Vue3 开发微信公众号/H5 中 JSSDK 的详细使用方式
一、准备工作
-
微信公众号配置
- 注册微信服务号(订阅号功能受限)
- 配置 JS 接口安全域名(如:
yourdomain.com
) - 获取
AppID
和AppSecret
(需保密)
-
项目环境
- uni-app 版本 ≥ 3.0
- Vue 版本 ≥ 3.2
- 确保 H5 域名与微信配置一致
二、JSSDK 集成步骤
1. 引入 JSSDK
在 index.html
中添加:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
<title>uni-app</title>
<!-- 引入微信 JSSDK -->
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>
2. 创建 JSSDK 初始化服务
// services/wx.js
import { ref } from 'vue';
import axios from 'axios';
// 缓存配置,避免重复请求
const isConfigured = ref(false);
// 获取签名配置
async function getSignature(url) {
try {
// 调用后端接口获取签名(需自行实现)
const response = await axios.get('/api/wx/signature', {
params: { url }
});
return response.data;
} catch (error) {
console.error('获取微信签名失败:', error);
return null;
}
}
// 初始化 JSSDK
async function initJssdk() {
// 避免重复初始化
if (isConfigured.value) return true;
// 获取当前 URL(不含 hash)
const url = window.location.href.split('#')[0];
// 获取签名配置
const config = await getSignature(url);
if (!config) return false;
// 配置 JSSDK
return new Promise((resolve, reject) => {
wx.config({
debug: false, // 生产环境关闭调试
appId: config.appId,
timestamp: config.timestamp,
nonceStr: config.nonceStr,
signature: config.signature,
jsApiList: [
'updateAppMessageShareData',
'updateTimelineShareData',
'chooseImage',
'getLocation',
'scanQRCode'
// 根据需要添加其他接口
]
});
wx.ready(() => {
isConfigured.value = true;
resolve(true);
});
wx.error((res) => {
console.error('微信 JSSDK 配置失败:', res);
reject(res);
});
});
}
export default {
initJssdk,
isConfigured
};
3. 后端签名接口实现(示例)
后端需提供接口生成签名,以下是 Node.js 示例:
// 后端接口(Node.js + Express)
const express = require('express');
const axios = require('axios');
const crypto = require('crypto');
const app = express();
// 配置
const appId = 'YOUR_APPID';
const appSecret = 'YOUR_APPSECRET';
let accessToken = '';
let jsapiTicket = '';
let tokenExpires = 0;
let ticketExpires = 0;
// 获取 AccessToken
async function getAccessToken() {
const now = Date.now();
if (now < tokenExpires) return accessToken;
const response = await axios.get(
`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appId}&secret=${appSecret}`
);
accessToken = response.data.access_token;
tokenExpires = now + (response.data.expires_in - 200) * 1000; // 提前200秒刷新
return accessToken;
}
// 获取 JSAPI Ticket
async function getJsapiTicket() {
const now = Date.now();
if (now < ticketExpires) return jsapiTicket;
const token = await getAccessToken();
const response = await axios.get(
`https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=${token}&type=jsapi`
);
jsapiTicket = response.data.ticket;
ticketExpires = now + (response.data.expires_in - 200) * 1000; // 提前200秒刷新
return jsapiTicket;
}
// 生成签名
function generateSignature(nonceStr, timestamp, url, ticket) {
const str = `jsapi_ticket=${ticket}&noncestr=${nonceStr}×tamp=${timestamp}&url=${url}`;
return crypto.createHash('sha1').update(str).digest('hex');
}
// 签名接口
app.get('/api/wx/signature', async (req, res) => {
try {
const url = req.query.url;
const nonceStr = Math.random().toString(36).substr(2, 15);
const timestamp = Math.floor(Date.now() / 1000);
const ticket = await getJsapiTicket();
const signature = generateSignature(nonceStr, timestamp, url, ticket);
res.json({
appId,
nonceStr,
timestamp,
signature
});
} catch (error) {
console.error('生成签名失败:', error);
res.status(500).json({ error: '生成签名失败' });
}
});
app.listen(3000, () => {
console.log('服务器启动在端口 3000');
});
三、常用功能实现
1. 分享功能
<template>
<view>
<button @click="setShareInfo">设置分享</button>
</view>
</template>
<script setup>
import wxService from '@/services/wx';
// 设置分享信息
const setShareInfo = async () => {
try {
// 确保 JSSDK 已初始化
await wxService.initJssdk();
// 设置分享到朋友圈
wx.updateTimelineShareData({
title: '分享标题',
link: 'https://yourdomain.com',
imgUrl: 'https://yourdomain.com/logo.png',
success: () => {
console.log('设置朋友圈分享成功');
},
fail: (res) => {
console.error('设置朋友圈分享失败:', res);
}
});
// 设置分享给朋友
wx.updateAppMessageShareData({
title: '分享标题',
desc: '分享描述',
link: 'https://yourdomain.com',
imgUrl: 'https://yourdomain.com/logo.png',
success: () => {
console.log('设置好友分享成功');
},
fail: (res) => {
console.error('设置好友分享失败:', res);
}
});
} catch (error) {
console.error('初始化 JSSDK 失败:', error);
}
};
</script>
2. 拍照/选择图片
<template>
<view>
<button @click="chooseImage">选择图片</button>
<image v-for="(img, index) in images" :key="index" :src="img" mode="aspectFit" />
</view>
</template>
<script setup>
import { ref } from 'vue';
import wxService from '@/services/wx';
const images = ref([]);
const chooseImage = async () => {
try {
await wxService.initJssdk();
wx.chooseImage({
count: 9, // 最多选择9张
sizeType: ['original', 'compressed'], // 原图或压缩图
sourceType: ['album', 'camera'], // 来源:相册或相机
success: (res) => {
const localIds = res.localIds; // 返回选定照片的本地ID列表
images.value = localIds;
// 如果需要上传到微信服务器
localIds.forEach((localId) => {
wx.uploadImage({
localId,
isShowProgressTips: 1,
success: (res) => {
const serverId = res.serverId; // 返回图片的服务器端ID
console.log('上传成功,serverId:', serverId);
// 可将 serverId 发送到自己的服务器保存
}
});
});
}
});
} catch (error) {
console.error('初始化 JSSDK 失败:', error);
}
};
</script>
3. 获取地理位置
<template>
<view>
<button @click="getLocation">获取位置</button>
<text v-if="location">纬度: {{ location.latitude }}, 经度: {{ location.longitude }}</text>
</view>
</template>
<script setup>
import { ref } from 'vue';
import wxService from '@/services/wx';
const location = ref(null);
const getLocation = async () => {
try {
await wxService.initJssdk();
wx.getLocation({
type: 'wgs84', // 默认为wgs84的gps坐标,可改为'gcj02'
success: (res) => {
location.value = {
latitude: res.latitude,
longitude: res.longitude,
speed: res.speed,
accuracy: res.accuracy
};
},
fail: (res) => {
console.error('获取位置失败:', res);
}
});
} catch (error) {
console.error('初始化 JSSDK 失败:', error);
}
};
</script>
4. 扫一扫功能
<template>
<view>
<button @click="scanQRCode">扫一扫</button>
<text v-if="scanResult">扫描结果: {{ scanResult }}</text>
</view>
</template>
<script setup>
import { ref } from 'vue';
import wxService from '@/services/wx';
const scanResult = ref('');
const scanQRCode = async () => {
try {
await wxService.initJssdk();
wx.scanQRCode({
needResult: 1, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果
scanType: ['qrCode', 'barCode'], // 可以指定扫二维码还是一维码
success: (res) => {
scanResult.value = res.resultStr;
}
});
} catch (error) {
console.error('初始化 JSSDK 失败:', error);
}
};
</script>
四、注意事项
-
URL 一致性
- 签名计算的 URL 必须与当前页面 URL 完全一致(不含 hash)
- 单页应用路由变化时需重新获取签名
-
错误处理
wx.error
回调捕获配置失败原因- 常见错误:签名错误、权限不足、URL 不匹配
-
版本兼容
- 不同版本 JSSDK 支持的接口不同
- 微信客户端版本需 ≥ 6.0.2
-
性能优化
- 签名配置可缓存(有效期 7200 秒)
- 按需初始化,避免重复请求
-
生产环境
- 关闭
debug: true
- 后端缓存 AccessToken 和 Ticket
- 错误日志上报
- 关闭
五、常见问题解决方案
-
签名错误
- 检查 URL 是否正确(不含 hash)
- 验证时间戳和随机字符串是否正确
- 检查 JSAPI Ticket 是否有效
-
接口调用失败
- 确认是否在
wx.ready
回调中调用 - 检查
jsApiList
是否已配置对应接口 - 微信版本是否支持该接口
- 确认是否在
-
单页应用问题
- 在路由变化时重新获取签名
- 使用全局变量缓存配置状态
-
跨域问题
- 确保域名已在微信公众平台配置
- 后端接口需处理跨域请求
通过以上步骤,您可以在 uni-app + Vue3 项目中顺利集成微信 JSSDK,实现丰富的原生功能。