⚠️ 很关键的前提 ⚠️
❗️个人开发者使用wx-jssdk的权限受限❗️(无转发给好友、分享朋友圈等权限),可先去「设置与开发-接口权限」查看权限详情。
♂️文末是我说的「体面」❗️
公众号设置
服务端代码
(基于Koa2开发,省略服务器搭建代码,仅提供关键步骤代码)
1.编写「服务器配置-服务器地址(URL)」所需要的接口地址
https://yourserver.com/api/ch...
let sha1 = require("sha1");
/**
* 微信公众号-指定服务器地址-验证接口
* @param {*} ctx
*/
checkToken: async (ctx) => {
const { signature, timestamp, nonce, echostr } = ctx.query;
//ctx.query获取请求中携带的参数
let { token } = wxconfig;
//将Token,timestamp,nonce按字典排序,排序后链接成一个字符串
let str = [token, timestamp, nonce].sort().join("");
//使用sha1模块进行sha1加密
let sha1Str = sha1(str);
//判断加密后的字符串与请求中signature是否相等
if (sha1Str === signature) {
ctx.body = echostr;
} else {
ctx.body = "Token check failed.";
}
};
这一步OK之后,在「微信公众号后台就能成功开启服务器了」。接下来就是编写H5在微信里调用wx-jssdk相关功能所需要的授权过程。其实主要就是拿到wxconfig所需要的一些必要参数。
编写wx.config所需数据的接口
https://yourserver.com/api/wx...需授权网站的url
生成wxconfig数据的大致流程:获取access_token
,通过access_token
获取jsapi_ticket
;然后拼接access_token
、jsapi_ticket
、noncestr
、timestamp
、要授权的页面的url
生成签名signature
,最终将appId
、timestamp
、nonceStr
、signature
返回给前端,前端调用wx.config
进行注册,成功后就可以在wx.ready
中调用相关js接口了。
wxConfig.js文件代码
!!!这里部分代码借鉴来的,懒得改了大致流程都是这样!
const sha1 = require("sha1");
const request = require("request");
const { wxconfig } = require("./wxConfig");
// 为了应对缓存压力,不要每次刷新token,访问量高会带来很大问题
// 因为获取access_token的接口,一天最多调用2000次,每次有效期是两个小时
let CACHE = {
ticket: "",
ticketTimeout: 0, //ticket过期时间
ticketTime: 0, //获取ticket时间
accessToken: "",
accessTokenTimeout: 0, //token过期时间
accessTokenTime: 0, //获取token时间
};
class wxModel {
/**
* 刷新access_token
*/
static async refreshAccessToken() {
return new Promise((resolve, reject) => {
const tokenUrl = `${wxconfig.getAccessTokenUrl}?grant_type=client_credential&appid=${wxconfig.appId}&secret=${wxconfig.appSecret}`;
request(tokenUrl, (error, response, body) => {
if (typeof body === "string") {
try {
body = JSON.parse(body);
} catch (e) {
body = {
errcode: "-1000",
body,
};
}
}
if (body && (!body.errcode || body.errcode == 0)) {
CACHE.accessToken = body.access_token;
CACHE.accessTokenTimeout = body.expires_in * 500;
CACHE.accessTokenTime = new Date();
resolve(CACHE.accessToken);
} else if (body) {
reject(body.errmsg);
} else {
reject("未知异常");
}
});
});
}
/**
* 刷新ticket
* @param {*} access_token
* @param {*} callback
*/
static async refreshJsapiTicket(access_token) {
// Jsapi_ticket
return new Promise((resolve, reject) => {
let ticketUrl = `${wxconfig.getJsapiTicketUrl}?access_token=${access_token}&type=jsapi`;
request(ticketUrl, function (err, response, content) {
content = JSON.parse(content);
if (content && (content.errcode == 0 || !content.errcode)) {
CACHE.ticket = content.ticket;
CACHE.ticketTimeout = content.expires_in * 500;
CACHE.accessTokenTime = new Date();
resolve(CACHE.ticket); // ticket
} else if (content) {
reject(content.errmsg);
} else {
reject("未知异常");
}
});
});
}
/**
* 获取wxconfig
* @param {*} url
* @returns
*/
static async geneWxConfig(url) {
// 获取access_token
let access_token = CACHE.accessToken;
let ticket = CACHE.ticket;
if (
!access_token ||
new Date() - CACHE.accessTokenTime > CACHE.accessTokenTimeout
) {
access_token = await this.refreshAccessToken();
ticket = await this.refreshJsapiTicket(access_token);
}
let nonceStr = this.createNonceStr();
let timestamp = this.createTimestamp();
let signature = this.createSign({
jsapi_ticket: ticket,
nonceStr,
timestamp,
url,
});
return {
appId: wxconfig.appId,
access_token,
ticket,
timestamp,
nonceStr,
signature,
};
}
/**
* 随机字符串
*/
static createNonceStr() {
return Math.random().toString(36).substr(2, 15);
}
/**
* 时间戳
*/
static createTimestamp() {
return parseInt(new Date().getTime() / 1000).toString();
}
/**
* 生成签名
* ⚠️ 只对url#前面部分加密
* ⚠️ noncestr全部小写
* @param {*} config
*/
static createSign(config) {
let ret = {
jsapi_ticket: config.jsapi_ticket,
nonceStr: config.nonceStr,
timestamp: config.timestamp,
url: config.url,
};
let url = ret.url.split("#")[0];
let string = `jsapi_ticket=${ret.jsapi_ticket}&noncestr=${ret.nonceStr}×tamp=${ret.timestamp}&url=${url}`;
let shaObjs = sha1(string);
return shaObjs;
}
}
module.exports = wxModel;
wxConfig.js文件内容
const wxconfig = {
appId: "wx9xxxxxxx",
appSecret: "xxxxxxxxxxxxxxxxxxxxx",
token: "hxxxt", //公众号后台自行配置的token
getAccessTokenUrl: "https://api.weixin.qq.com/cgi-bin/token",
getJsapiTicketUrl: "https://api.weixin.qq.com/cgi-bin/ticket/getticket",
};
module.exports = {
wxconfig,
};
前端代码
1.引入wx-jssdk
let jssdkUri = `${window.location.protocol}//res.wx.qq.com/open/js/jweixin-1.6.0.js`;
loadJs() {
return new Promise((resolve, reject) => {
if (window.wx) {
return resolve(window.wx);
}
let script = document.createElement("script");
script.type = "text/javascript";
script.src = this.jssdkUri;
window.document.getElementsByTagName("head")[0].appendChild(script);
script.onload = () => {
resolve(window.wx);
};
script.onerror = reject;
});
}
2.请求 /api/wxconfig
接口,拿到必要数据后调用 wx.config
let url = window.location.href;
let data = await this.getWxconfig(url);
wx.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: data.appId, // 必填,公众号的唯一标识
timestamp: data.timestamp, // 必填,生成签名的时间戳
nonceStr: data.nonceStr, // 必填,生成签名的随机串
signature: data.signature, // 必填,签名
jsApiList: [
"hideMenuItems",
"updateAppMessageShareData",
"updateTimelineShareData",
], // 必填,需要使用的JS接口列表
});
3.在wx.ready中编写「分享、转发」和其它业务需要的方法
share({ title, desc, link, imgUrl }) {
this.shareData = { title, desc, link, imgUrl };
if (!this.isReady) {
console.error("wx jssdk is not ready.");
return;
}
wx.ready(() => {
wx.updateAppMessageShareData({
title,
desc,
link,
imgUrl, // 分享图标
success: (res) => {},
fail: (err) => this.onError(err),
});
wx.updateTimelineShareData({
title,
link,
imgUrl, // 分享图标
success: (res) => {},
fail: (err) => this.onError(err),
});
});
}
hideMenu() {
if (!this.isReady) {
console.error("wx jssdk is not ready.");
return;
}
wx.ready(() => {
wx.hideMenuItems({
menuList: [
"menuItem:share:appMessage",
"menuItem:share:timeline",
"menuItem:share:qq",
"menuItem:share:weiboApp",
"menuItem:share:QZone",
"menuItem:copyUrl",
],
});
});
}
如何让H5在微信里活的「体面」!
最后想说一下,作为一个个人开发者无法调用wx.updateAppMessageShareData
和wx.updateTimelineShareData
等一些接口,我是怎么利用有限的功能让我的H5网页尽量「体面」的吧!
既然不能分享,那干脆干掉微信点击右上角功能按钮后弹出来的「转发给朋友」和「分享到朋友圈」,甚至干掉「复制链接」功能菜单。
这样尽量减少转发出去只剩下裸露的难看的url链接的情况发生。
至于如何传播这个H5网页?给H5生成一个二维码,贴在一张好看的图片上,转发给朋友吧哈哈哈!
顺便附上一个Amazing的免费在线 二维码生成网站