前言
最近对于工程的微信登录逻辑进行了重构,进行了如下思考。
工程简介
工程是一个后台管理系统,使用vue框架客户端渲染,其中有一个模块运行在手机端,并且涉及到微信登录,在早期的设计中,对于微信浏览器内的登录是这样处理的:
- 微信浏览器或者普通浏览器打开分享的链接
- 如果是非登录状态,那么在请求接口的过程中因为网络请求拦截器的作用会将页面重定向到登录页
- 在登录页进行判断,如果是普通浏览器则手动登录,微信浏览器则自动登录
- 登录成功后会重定向到之前的页面
为什么之前会这样处理呢,原因在于,此工程的登录流程会设计到多个状态的查询与处理,例如:
- 微信登录后检查用户信息,如果用户没有绑定手机号,那么弹出绑定手机号的模态框进行绑定手机号
- 手机号码登录后,如果用户没有绑定微信,那么弹出微信绑定的模态框,要求用户绑定微信
这样过于复杂的一系列需求,不太利于做成全局的处理,当时也没想到好的解决方案,但是这样来回的跳转,对于用户体验不够友好,近期对项目进行重构时,决心改善这一问题。
解决方案
提炼公共逻辑
对于微信登录,获取微信授权链接的跳转以及微信返回code的处理是一个通用的逻辑,对此做了如下封装
export function wxLogin(callback) {
const code = getQueryString(window.location.href, 'code')
if (!code) {
window.location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${process.env.WX_APP_ID}&response_type=code&scope=snsapi_userinfo&state=onfuckweixin&redirect_uri=${escape(`${window.location.href}`)}#wechat_redirect`
} else {
axios.post(IP + '/login/wxauth', {
code: code
}).then(data => {
if (!data.phone_bind) {
bindPhone()
} else {
location.href = replaceQueryString('code', '')
callback()
}
}).catch(e => {
vue.$notify.error({
title: '错误',
message: '微信登陆失败'
})
})
}
function bindPhone() {
const comp = new BindComponent().$mount()
document.body.appendChild(comp.$el)
comp.open({
type: 'phone',
success: () => {
location.href = replaceQueryString('code', '')
window.location.reload()
}
})
}
}
复制代码
大致的意思是,进行微信登录时,先判断url是否已经有code,有code说明已经获得授权,那么直接将code发送给服务器进行登录就好,如果没有code信息,那么说明是未授权,则跳转微信授权地址,获取授权,值得一题的是这一段逻辑:
import Vue from 'vue'
import Bind from '@/components/bind/bind'
const BindComponent = Vue.extend(Bind)
...
function bindPhone() {
const comp = new BindComponent().$mount()
document.body.appendChild(comp.$el)
comp.open({
type: 'phone',
success: () => {
location.href = replaceQueryString('code', '')
window.location.reload()
}
})
}
复制代码
因为之前在编写绑定相关逻辑时有先见之明,提前封装了绑定的组件,所以这里可以很方便的进行调用,当服务器返回该用户没有绑定手机的信息时,则实例化绑定组件并将dom渲染到页面(这种做法是UI组件库常用的逻辑)绑定成功后,删除url中的code字段,并且刷新页面,为什么要刷新页面呢?看如下代码:
//main.js
const bootstrap = function() {
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
}
if (isWX() && getQueryString(window.location.href, 'code')) {
wxLogin(bootstrap)
} else {
bootstrap()
}
复制代码
在工程的入口页面对于根vue组件的实例化之前进行了一次判断:
如果是微信浏览器,并且url中附带了code信息,那么进入微信登录的逻辑,否则进行页面渲染
如果登录成功且已经绑定过手机,执行回调函数也就是实例化根vue:
else {
location.href = replaceQueryString('code', '')
callback()
}
复制代码
其实这里有点递归的意思,需要各位自行理解,不做详细说明。
既然入口文件只有在获取到code的前提下才进行微信登录,那么如果当前url没有code并且需要使用微信登录该怎么办?解决方案如下:
repInterceptor: [function(response) {
try {
if (response.data.success) {
return response.data.data
} else if ([401, 403].includes(response.data.error_code)) {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
loginCheck(response.data.error_text)
}, 500)
}
return Promise.reject(response.data)
} catch (e) {
return Promise.reject(e)
}
}, function(e) {
// Do something with response error
return Promise.reject({ error_text: '网络错误: ' + e })
}]
复制代码
如上是此工程的网络拦截器,如果返回401或者403说明用户在未登录的前提下请求了限制接口,那么会执行 loginCheck,而在loginCheck函数中有这样一段逻辑:
if (isWX()) {
wxLogin()
}
复制代码
一切又回到了宇宙的中心 wxLogin ==~