问题描述
你一般会把 login 的请求放在哪里?大部分人都应该是在 app.js 中 onLaunch 中做登录处理,如果仅仅是这样做登录是会存在一个问题。login 接口一定是异步返回的,那么进入业务的页面如 index 页面也有请求,业务请求大部分需要在 login 接口返回后才能去请求。但是 onLaunch 是不能延迟加载。
那么问题就来了 index 页面如何在正确的时机去发送请求?有些页面因为是需要被分享出去的,是不用依赖登录,大部分是一定要 token 回来之后才能发送请求。
网上的一些解决方案
- emit 在登录完成后触发一个自定义事件通知到页面登录完成。index 页面监听这个事件。 弊端:如果有几个需要第一时间被加载的页面就要监听几次。维护困难
- 下载插件,网上有针对这种问题有部分写一些库例如: https://developers.weixin.qq.com/community/develop/article/doc/00082263c94fe0a3559381c876bc13 我觉得没必要因为这一小点然后就下载一个库,本着万一出现问题还没有无从排查。小程序这一块还是尽量少下库,毕竟本身这一块的生态并不健全,还是把风险控制到最小,比较安全。
我的解决方案
我是从封装 request 请求入手。
- App 里维护一个全局 loginPromise
- 所有业务请求前先 await ensureLogin()
- 首次会真正发登录;并发请求会等待同一个 Promise
- 登录完成后再放行业务请求
也可以根据自己需要加入白名单,如果不需要 token 的接口 例如分享到朋友圈的落地页,不需要登录就可以请求
// app.js
App({
globalData: {
token: '',
loginPromise: null,
},
ensureLogin() {
if (this.globalData.token) return Promise.resolve(this.globalData.token);
if (this.globalData.loginPromise) return this.globalData.loginPromise;
this.globalData.loginPromise = new Promise((resolve, reject) => {
wx.login({
success: ({ code }) => {
wx.request({
url: 'https://api.example.com/login',
method: 'POST',
data: { code },
success: (res) => {
const token = res.data.token;
this.globalData.token = token;
resolve(token);
},
fail: reject,
complete: () => {
this.globalData.loginPromise = null;
},
});
},
fail: (err) => {
this.globalData.loginPromise = null;
reject(err);
},
});
});
return this.globalData.loginPromise;
},
});
// utils/request.js
const app = getApp();
function request(options) {
return app.ensureLogin().then((token) => {
return new Promise((resolve, reject) => {
wx.request({
...options,
header: {
...(options.header || {}),
Authorization: `Bearer ${token}`,
},
success: resolve,
fail: reject,
});
});
});
}
module.exports = { request };