页面用浏览器自带返回和安卓物理返回死循环的话,直接看高潮部分
背景
折磨我两个工作日加周末一天的问题,我觉得还是有必要记录一下,为什么程序员总是加班,就是遇到这些意想不到的问题
需求
领导:我想做两个页面,放在微信里面可以访问
我:简单啊,用 H5 实现
产品设计中 …
产品:好了,看看没问题就开始开发吧
我:什么时候多了一个需要获取用户信息(产品总是给人惊喜不端)
没有试过微信授权这一块,首先内心三连问,能不能拒绝,能不能改需求,能不能通过熟悉的秘方实现;然而并没有什么用(笑哭表情)
开发中
业务功能没什么难点,模拟一个用户信息,很快就开发完成了 …
微信网页授权
官方文档:https://mp.weixin.qq.com/wiki…
1. 登录自己的服务号,查看已有的权限
注意:这里只能是服务号,订阅号没有权限,服务号只能由企业和组织申请
2. 公众号设置
注意:授权回调域名配置规范为全域名,比如需要网页授权的域名为:www.qq.com,配置以后此域名下面的页面 http://www.qq.com/music.html、http://www.qq.com/login.html 都可以进行 OAuth2.0 鉴权。但 http://pay.qq.com、http://music.qq.com、http://qq.com 无法进行 OAuth2.0…
注意:下载这个文件放在域名对应的根目录下
3. 链接的生成
https://open.weixin.qq.com/co…
以访问 www.qq.com/#/detail.html 为例,上面第 2 步的网页授权域名设置成 www.qq.com
REDIRECT_URI 为 encodeURIComponent(‘http://www.qq.com/#/detail.html’)
SCOPE 为 snsapi_base 或 snsapi_userinfo, 区别是只获取用户 id,还是获取更多用户信息
state 随便写吧,以 123 为例(没有看出具体有什么用)
其它参数不变
注意:为什么要用 encodeURIComponent,你想想,你在 url 里面直接写 url,怎么能直接把它解析成参数
4. 放在微信里访问
把第 3 步上面生成的链接,做为聊天消息,拷贝到微信里面,点击打开,此时我们用 alert(location.href)就会发现,地址变成了 http://www.qq.com/?code= 生成 …
// 获取 url 参数
export function getQueryVariable(variable) {const query = window.location.search.substring(1)
const vars = query.split("&")
for (let i=0; i<vars.length; i++) {const pair = vars[i].split("=")
if(pair[0] === variable) return pair[1]
}
return undefined
}
// 获取 code 的值
const code = getQueryVariable('code')
注意:code 只能用一次,而且还有时间限制,code 插入的位置尤其要注意,不是在
/#/ 后面,所以用 vue 或 react 的路由组件中获取路由参数方式是不可行的,老老实实的写原生 js 获取
5. 通过 code 获取用户 id
code 获取到了以后前端就无能为力了,接下来的步骤只能交给后台了(数据安全性考虑,比如 AppSecret 不能暴露给前端),自古以来,前端的地位略低于后端,nodejs 的出现极大的拯救了前端,虽然后面的事情前端做不了,但我们可以用 nodejs 或者是类似于 postman 这种的工具,把后面的接口模拟跑通,然后直接告诉后端,你该调什么接口,用什么参数,后端文档都不用看;当然你也可以做个小白纸,什么都抛给后端,让后端指挥你怎么做(在一个团队中,各个角色的重要性,就看 能做的 事情)
网页授权就这么搞完了,是不是很简单(笑哭表情),可以愉快的玩耍了
当我把这个链接通过微信右上角分享给同事一起测试时,啪啪打脸了 …,呈现的现象
1. 通过 code 获取用户 id,报错了
2. 分享标题,描述,图标好丑,我能不能改(好像没有分享这个需求,但不分享的话,用户怎么看,我要不要做,没有这个需求,我要不要把锅丢给产品,我好方,但仅有的一点点职业素养告诉我,我还是做吧,做为一个合格的程序员,必备的技能不就是脑补一些产品没想到需求吗(笑哭表情))
分析原因:
1. 打印出分享的地址 alert(location.href),为 http://www.qq.com/?code= 上一 …,微信分享出去的是当前访问的地址(原滋原味,加了一些防腐剂,多了一个 from=xxxx,应该是来源),但我理想中想要的是上面第 3 中的地址(重定向之前的地址),报错的原因是同一个 code 用了两次
解决思路:
1. 自定义分享地址,此时的思路是,地址改成 https://open.weixin.qq.com/co… 这种
2. 自定义标题,描述和图片
3. 最终方案看下面
微信网页分享
官方文档:https://mp.weixin.qq.com/wiki…
1. 还是先配置相应的权限
2. 生成签名(后端做的,前端可以选择跳过)
这个时候就不得不吐槽微信的文档了,残缺了,在第一步就卡住了,只能求助网友,后面我发现
这里有,IP 白名单也要设置,否则是拿不到 access_token 的,其它的签名怎么生成的后端照着文档做就行了
注意点:网页授权和分享是两个完全独立的模块,分享的 access_token 和授权返回的 access_token 是完全不一样的概念
3. 前端配置
yarn add weixin-js-sdk
此时我的版本是 ”weixin-js-sdk”: “^1.4.0-test”, 微信客户端的版本是 7.0.4,竟然遇到一个大坑
微信官方让我用最新的接口,我试了很久都没有调通,总以为是自己的姿势不对,后面实在不行了,我试了一下老接口,竟然通了,竟然通了,竟然通了,我想崩溃了
上代码
wx.config({
debug: false, // 开启调试模式,调用的所有 api 的返回值会在客户端 alert 出来,在 pc 端时会打印出来,不需要的话可以将 true 改成 false。appId: data.appid, // 必填,公众号的唯一标识
timestamp: data.timestamp, // 必填,生成签名的时间戳
nonceStr: data.nonceStr, // 必填,生成签名的随机串
signature: data.signature, // 必填,签名
jsApiList: ['onMenuShareAppMessage','onMenuShareTimeline','onMenuShareQQ','onMenuShareQZone']
})
wx.ready(function () {
// 分享到朋友圈
wx.onMenuShareTimeline({
title, // 分享标题
link, // 分享链接,该链接域名或路径必须与当前页面对应的公众号 JS 安全域名一致
imgUrl, // 分享图标
})
// 分享给朋友
wx.onMenuShareAppMessage({
title, // 分享标题
desc, // 分享描述
link, // 分享链接,该链接域名或路径必须与当前页面对应的公众号 JS 安全域名一致
imgUrl, // 分享图标
})
// 分享到 QQ
wx.onMenuShareQQ({
title, // 分享标题
desc, // 分享描述
link, // 分享链接,该链接域名或路径必须与当前页面对应的公众号 JS 安全域名一致
imgUrl, // 分享图标
})
// 分享到 QQ 空间
wx.onMenuShareQZone({
title, // 分享标题
desc, // 分享描述
link, // 分享链接,该链接域名或路径必须与当前页面对应的公众号 JS 安全域名一致
imgUrl, // 分享图标
})
})
注意:
1.data 是后台返回的签名信息
2. 分享链接,该链接域名必须与当前页面对应的公众号 JS 安全域名一致,公众号里面配置的是 www.qq.com,这里分享的地址只能是以 www.qq.com 开头的,和我们理想的 https://open.weixin.qq.com/co… 开头差距很大(怎么授权,好方)
如果只是单纯的自定义分享,到这里就结束了,如果既要分享又要授权,又有问题需要解决了
4. 重定向
- 初始打开地址为:http://www.qq.com/#/detail.html,这个时候不带 code
- 进入页面之后。location.href = ‘https://open.weixin.qq.com/co…’(下面都称为重定向)
- 页面地址变成了 http://www.qq.com/?code= 生成 …
- 用的是同一个页面,所以需要判断 url 有没有 code,来决定是否要执行第 2 步,否则会死循环
文章写到这里,已经解决了授权 + 分享,是不是感觉故事很平稳,一部好的电影怎么能这么快结束
高潮(大坑来了)
现象
微信中打开页面,用安卓的物理返回键,点一下返回不了,需要连续快速点两下才能退出
分析原因
打开的页面是不带 code 的地址,经过重定向后,生成了带 code 的地址,此时浏览器的历史记录中会有两条记录,从带 code 的地址返回到不带 code 的地址,页面判断没有带 code,又会重定向到带 code 的页面,产生了死循环
解决方案
1. 不让它产生多一条的记录
首先想到的是 location.replace(url), 发现把 url 替换成授权的地址时(https://open.weixin.qq.com/co…),并不会像想象中的那样工作,还是会有两条历史记录(不清楚微信为什么不解决,还是解决不了)
结果:失败
2. 代码控制清除一条历史记录
const params = getQueryParams()
const code = sessionStorage.getItem('code')
if (!params.code && !code) {let url = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${yourAppId}&redirect_uri=${encodeURIComponent(location.href)}&response_type=code&scope=snsapi_base&state=1#wechat_redirect`
window.location.replace(url)
} else if(!code){sessionStorage.setItem('code', params.code)
history.back()}
搜索了一大圈,看见了这段代码(来自:https://www.cnblogs.com/wonyu…),仿佛看见了光,里面比较讲究的就是
sessionStorage(只在当前会话有效), 用当前会话窗口储存的 code,来判断不带 code 的页面要不要重定向
看似很完美的代码,还是逃不过微信的大坑,通过链接进入时,偶尔会遇到页面空白
原因:
上面用到的历史记录返回,页面不刷新(偶尔),我尝试过强制微信浏览器刷新,甚至怀疑是 vue 的坑,然后用原生写了一段 js 来操作 dom,并没有什么用
最后发现返回的时候,js 都能正常的执行,UI 不重新渲染,UI 不重新渲染,UI 不重新渲染,偶尔出现,不是必现,我再次好方
结果:偶尔失败
3. 返回两次,才能退出
借鉴上面的思路,判断当前会话窗口储存的 code 是否有值,有值就不重定向
结果:解决了偶尔不渲染的问题和死循环的问题,但对于有轻微强迫症的我来说,需要点两次才能返回还是挺难接受的
本来属于愉快的周末,就在微信这个大坑里结束了,关上电脑,沉思一会(想想如何说服测试同学这个问题解决不了,是微信的坑(笑哭表情))
结尾
处女座最大的悲哀就是心里不能放事情,总感觉不踏实,休息也休息不好(悲哀)
突然想到前段时间开发的 app,自己控制过 android 的返回,微信会不会也提供了自定义返回呢,经过一大堆的搜索,发现 js 就有能监听浏览器返回的事件(有些知识点不用,慢慢就忘了),兜兜转转了一大圈,至少用我的周末帮大家证明了网上说的很多方式行不通,哈哈哈,终极解决,上代码(vue 的思路)
created () { // 当前需要直接退出的页面
window.history.pushState(null, null, "")
window.addEventListener("popstate", this.popstate, false)
},
destroyed: function () { // 记得清除,不然单页面,其它页面也可以用
window.removeEventListener("popstate", this.popstate, false)
},
methods: {popstate () {wx.closeWindow() // 微信网页退出
}
}
注意:为什么要多 window.history.pushState(null, null, “”)这句代码,因为 popstate 只能监听 pushState 创建的
备注:微信的文档会更新,当你使用时(看一下这篇文章的出生时间),最好参考官方文档,如果你觉得有一点帮助了你,请点个赞(程序员写文章不容易,最烦写这种随时可能过时的文章,哈哈哈)