前期准备
①微信开放标签可以让第三方页面直接唤起App,避免了通过右上角浏览器中打开再唤起App的麻烦操作。一切按照官方文档说明开发。
②安装微信开发者工具。
③一个官方认证的公众服务号,且对应App绑定了该微信服务号,支持跳转。不可使用微信公众平台的测试公众号,否则无法展示开放标签!但是SDK中提供的接口可以在测试号上进行调试。
④如果使用微信公众平台测试号,需要配置JS接口安全域名和网页授权获取用户基本信息,填写正确的域名和端口号。
⑤本文使用测试号来演示开放标签<wx-open-launch-app>
的使用。前端页面使用VSCode的Live Server
本地开启服务器,端口号是5500
。后端使用Node的express
本地开启服务器,端口号是3000
。再次注意! 测试号无法展示出开放标签。
前端
注入微信JS-SDK
①请求的微信JS-SDK跨域,所以使用JSONP方法获取SDK脚本,可以使用fetchJsonp第三方库。
②获取页面url、随机字符串nonceStr、时间戳timestamp,发送给后端获取签名signature。
③签名的自验可以通过该链接。
// JSONP
function jsonp(url) {
return new Promise((resolve, reject) => {
const script = document.createElement("script");
script.setAttribute("src", url);
document.getElementsByTagName("head")[0].appendChild(script);
script.onload = resolve();
});
}
// 微信JS-SDK
const wxsdkURL = "//res.wx.qq.com/open/js/jweixin-1.6.0.js";
// 获取签名
const getSignature = (str, time) => {
const url = encodeURIComponent(location.href.split("#")[0]);
return ajax.get(
`${location.protocol}//${location.hostname}:3000/sdk?noncestr=${str}×tamp=${time}&url=${url}`
);
};
初始化微信
①将上一步获取的nonceStr、timestamp和signature传给window.wx.config()
,并设置需要调用的API。
// 初始化微信
const initWeChat = (nonceStr, timestamp, signature) => {
return new Promise((resolve, reject) => {
window.wx.config({
debug: false,
appId: appId,
timestamp,
nonceStr,
signature,
jsApiList: [
"updateAppMessageShareData",
"updateTimelineShareData",
"onMenuShareWeibo",
"chooseImage",
"uploadImage",
"downloadImage",
"previewImage",
"openLocation",
"getLocation",
"closeWindow",
"scanQRCode",
],
openTagList: ["wx-open-launch-app"],
});
window.wx.ready(() => resolve(window.wx));
window.wx.error(reject);
});
};
// 整体流程
export const initWxSDK = () => {
return jsonp(wxsdkURL).then(() => {
const nonceStr = randomString();
const timestamp = Math.floor(Date.now() / 1000);
return getSignature(nonceStr, timestamp).then((signature) =>
initWeChat(nonceStr, timestamp, signature)
);
});
};
使用开放标签和SDK
①开放标签和微信的SDK必须在微信初始化成功后调用,且必须在微信平台。
const scan = document.getElementById("scan");
// 微信扫一扫事件
const handlescan = () => {
wx.scanQRCode({
needResult: 0,
scanType: ["qrCode", "barCode"],
success: function (res) {
console.log(res.resultStr);
},
});
};
// 调用微信SDK设置右上角分享,开启扫一扫
initWxSDK().then((wx) => {
const params = {
title: "测试分享",
desc: "这里是描述",
link: shareURL,
imgUrl: "",
};
wx.updateAppMessageShareData(params);
wx.updateTimelineShareData(params);
wx.onMenuShareWeibo(params);
showOpenTag();
scan.addEventListener("click", handlescan);
});
开放标签样式
①开放标签的样式比较独特,<script>标签内外样式隔离,最好对开放标签外包一层<div>。
/* 开放标签 */
.wxOpenTag {
position: fixed;
z-index: 99999;
bottom: 90px;
left: 0;
right: 0;
margin: 0 auto;
display: flex;
justify-content: center;
}
const showOpenTag = () => {
const openTag = document.createElement("div");
// 开放标签容器样式
openTag.className = "wxOpenTag";
document.getElementsByTagName("body")[0].appendChild(openTag);
// 开放标签样式
const style = {
width: `${transformPx(160)}px`,
height: `${transformPx(40)}px`,
fontSize: `${transformPx(14)}px`,
color: "#fff",
display: "flex",
justifyContent: "center",
alignItems: "center",
backgroundColor: "#37f",
borderRadius: `${transformPx(8)}px`,
};
openTag.innerHTML = `
<wx-open-launch-app appid=${appId} id="openTag" extinfo=${"someInfo"}>
<script type="text/wxtag-template"></script>
</wx-open-launch-app>
`;
const ref = document.getElementById("openTag");
const btn = document.createElement("div");
addStyle(btn, style);
btn.innerHTML = "App内打开";
ref.children[0].appendChild(btn);
// 开放标签绑定事件
const launch = (e) => console.log("success");
const error = (e) => console.log(e.detail);
ref.addEventListener("launch", launch);
ref.addEventListener("error", error);
};
后端
获取公众号的access_token
// 获取AccessToken
const getAccessToken = () => {
return new Promise((resolve, reject) => {
const URL = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appId}&secret=${appSecret}`;
https
.get(URL, (res) => {
let rawData = "";
res.on("data", (data) => (rawData += data));
res.on("end", () => resolve(JSON.parse(rawData)));
})
.on("error", (e) => console.log(e.message));
});
};
通过AccessToken获取jsapi_ticket
// 通过AccessToken获取jsapi_ticket
const getJsapiTicket = (access_token) => {
return new Promise((resolve, reject) => {
const URL = `https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=${access_token}&type=jsapi`;
https
.get(URL, (res) => {
let rawData = "";
res.on("data", (data) => (rawData += data));
res.on("end", () => resolve(JSON.parse(rawData)));
})
.on("error", (e) => console.log(e.message));
});
};
整体流程
①最好将access_token
和jsapi_ticket
进行全局缓存,并且定时(2小时)刷新。
// 整体流程
const wxsdk = async () => {
if (!cache.access_token) {
const token = await getAccessToken();
// 请求报错
if (!token.access_token) {
return res.send({ code: token.errcode, message: token.errmsg });
}
cache.access_token = token.access_token;
console.log("已缓存access_token: ", cache.access_token);
}
if (!cache.jsapi_ticket) {
const jsapi_ticket = await getJsapiTicket(cache.access_token);
// 请求报错
if (jsapi_ticket.errcode) {
return res.send({
code: jsapi_ticket.errcode,
message: jsapi_ticket.errmsg,
});
}
cache.jsapi_ticket = jsapi_ticket.ticket;
console.log("已缓存jsapi_ticket: ", cache.jsapi_ticket);
}
const { noncestr, timestamp, url } = req.query;
const string = `jsapi_ticket=${
cache.jsapi_ticket
}&noncestr=${noncestr}×tamp=${timestamp}&url=${decodeURIComponent(
url
)}`;
const signature = createHash("sha1").update(string).digest("hex");
return res.send({
code: 0,
message: "success",
data: signature,
});
};
wxsdk();