微信公众号自定义分享功能的Vue实现指南

1.需求概述
在公众号开发过程中,业务部门经常对微信内置的分享功能(右上角->分享给朋友/朋友圈)效果不满意,希望能够自定义分享的样式和内容。
1.1微信默认分享效果

1.2自定义分享效果

1.3微信官方文档
JS-SDK说明文档: https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html
2.实现步骤
2.1后端实现
⭐2.1.1接口控制器
@PostMapping("/signature")
public ResponseData getSignature(@RequestParam String url, HttpServletResponse response, HttpServletRequest request) throws Exception {
ResponseData result = ResponseData.success();
String jsapiTicket = wechatService.getJsapiTicket();
if (StringUtils.isEmpty(jsapiTicket)) {
result.setCode(500);
result.setMessage("获取签名失败");
return result;
}
String randomStr = UUID.randomUUID().toString();
long currentTime = System.currentTimeMillis() / 1000;
StringBuilder signStr = new StringBuilder();
signStr.append("jsapi_ticket=").append(jsapiTicket)
.append("&noncestr=").append(randomStr)
.append("×tamp=").append(currentTime)
.append("&url=").append(url);
String signature = generateSHA1(signStr.toString());
Map<String, Object> data = new HashMap<>();
data.put("url", url);
data.put("jsapi_ticket", jsapiTicket);
data.put("nonceStr", randomStr);
data.put("timestamp", currentTime);
data.put("signature", signature);
data.put("appId", WechatConfig.APP_ID);
result.setData(data);
return result;
}
private String generateSHA1(String str) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.reset();
digest.update(str.getBytes("UTF-8"));
return bytesToHex(digest.digest());
} catch (Exception e) {
throw new RuntimeException("SHA1签名生成失败", e);
}
}
private String bytesToHex(byte[] bytes) {
StringBuilder hexString = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
⭐2.1.2微信服务类
public String getJsapiTicket() {
String cacheKey = WechatConfig.JSAPI_TICKET_CACHE_KEY;
// 从缓存获取
if (redisTemplate.hasKey(cacheKey)) {
return redisTemplate.opsForValue().get(cacheKey);
}
// 从服务器获取
return fetchJsapiTicketFromServer();
}
private synchronized String fetchJsapiTicketFromServer() {
String cacheKey = WechatConfig.JSAPI_TICKET_CACHE_KEY;
// 再次检查缓存,防止并发请求重复获取
if (redisTemplate.hasKey(cacheKey)) {
return redisTemplate.opsForValue().get(cacheKey);
}
try {
String accessToken = accessTokenService.getAccessToken();
if (StringUtils.isEmpty(accessToken)) {
return null;
}
String ticketUrl = String.format(WechatConfig.JSAPI_TICKET_URL, accessToken);
String response = httpService.executeGet(ticketUrl);
if (StringUtils.isBlank(response)) {
return null;
}
JSONObject json = JSON.parseObject(response);
if (json.containsKey("ticket")) {
String ticket = json.getString("ticket");
int expiresIn = json.getIntValue("expires_in");
// 存入缓存,设置过期时间(提前10分钟过期)
redisTemplate.opsForValue().set(cacheKey, ticket, expiresIn - 600, TimeUnit.SECONDS);
return ticket;
}
} catch (Exception e) {
log.error("获取JSAPI Ticket失败", e);
}
return null;
}
⭐2.1.3HTTP请求工具类
public String executeGet(String url) {
StringBuilder response = new StringBuilder();
try {
URL requestUrl = new URL(url);
HttpURLConnection connection = (HttpURLConnection) requestUrl.openConnection();
// 配置连接参数
connection.setRequestMethod("GET");
connection.setConnectTimeout(8000);
connection.setReadTimeout(15000);
connection.setUseCaches(false);
// 获取响应
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream(), "UTF-8"))) {
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
}
}
} catch (Exception e) {
log.error("HTTP GET请求失败: " + url, e);
}
return response.toString();
}
重要提示: 由于微信获取api_ticket接口调用次数有限制,建议将api_ticket缓存在本地服务器(如Redis)中,避免频繁请求微信接口
2.2前端实现(Vue)
⭐2.2.1公共工具方法封装
/**
* 初始化微信JS-SDK分享功能
* @param {Object} shareConfig 分享配置对象
* @param {String} shareConfig.link 分享链接
* @param {String} shareConfig.title 分享标题
* @param {String} shareConfig.desc 分享描述
* @param {String} shareConfig.imgUrl 分享图片URL
*/
function initWeChatShare(shareConfig) {
// 获取当前URL(去除hash部分)
const currentUrl = window.location.href.split('#')[0];
// 请求后端获取签名
api.post('/api/wechat/signature', {
url: currentUrl
}).then(response => {
if (response.success) {
const { appId, timestamp, nonceStr, signature } = response.data;
// 需要使用的JS接口列表
const jsApiList = [
'updateAppMessageShareData',
'updateTimelineShareData'
];
// 配置微信JS-SDK
wx.config({
debug: false, // 关闭调试模式
appId: appId, // 公众号唯一标识
timestamp: timestamp, // 签名时间戳
nonceStr: nonceStr, // 签名随机串
signature: signature, // 签名
jsApiList: jsApiList // 需要使用的JS接口列表
});
// 监听JS-SDK初始化完成事件
wx.ready(() => {
// 自定义好友分享
wx.updateAppMessageShareData({
title: shareConfig.title,
desc: shareConfig.desc,
link: shareConfig.link,
imgUrl: shareConfig.imgUrl,
success: () => {
console.log('好友分享配置成功');
}
});
// 自定义朋友圈分享
wx.updateTimelineShareData({
title: shareConfig.title,
link: shareConfig.link,
imgUrl: shareConfig.imgUrl,
success: () => {
console.log('朋友圈分享配置成功');
}
});
});
// 监听JS-SDK初始化失败事件
wx.error((err) => {
console.error('微信JS-SDK初始化失败:', err);
});
}
}).catch(error => {
console.error('获取微信签名失败:', error);
});
}
⭐2.2.2组件中使用
export default {
name: 'SharePage',
data() {
return {
shareConfig: {
link: window.location.href,
title: '自定义分享标题',
desc: '自定义分享描述信息',
imgUrl: 'https://example.com/share-image.jpg'
}
};
},
mounted() {
// 在组件挂载后初始化分享功能
this.initShare();
},
methods: {
initShare() {
// 确保在用户可能点击分享按钮前调用
initWeChatShare(this.shareConfig);
}
}
};
重要提示: 必须在用户可能点击分享按钮前调用初始化方法,确保JS-SDK配置完成
