共计 5703 个字符,预计需要花费 15 分钟才能阅读完成。
我的公众号原文地址 📖
⚠️ 很要害的前提 ⚠️
❗️集体开发者应用 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 的收费在线 二维码生成网站