介绍
在微信小程序开发中,通常在 App.js 文件的 onLaunch 方法中获取登录凭证(以下称为:token),然后在 Page.js 文件的 onLoad 方法中执行页面数据请求。由于获取 token 和页面数据请求都是异步的,控制这两个方法的执行顺序可能会有困难,常常导致在页面请求数据时无法获取到 token。
本文介绍了如何利用事件广播和请求拦截器来实现对获取 token 和页面数据请求的顺序控制,而且无需在每个页面都进行操作。
实现逻辑
实现代码
event.js
//事件广播
const eventListeners = new Map();
const registerEventListener = (eventName, callback) => {
if (!eventListeners.has(eventName)) {
eventListeners.set(eventName, []);
}
eventListeners.get(eventName).push(callback);
};
const emitEvent = (eventName, data) => {
const listeners = eventListeners.get(eventName);
if (listeners) {
listeners.forEach(listener => {
listener(data);
});
}
};
module.exports = {
emitEvent,
registerEventListener
}
Token.js
//TOKEN操作封装
import { emitEvent } from './event' //广播事件
/**
* 设置 token
* @param {object} tokenData - 包含 accessToken 和 expires 的对象。
* @param {string} tokenData.accessToken - 访问令牌。
* @param {string} tokenData.expires - 令牌过期时间。
*/
export const setToken = async ({ accessToken, expires }) => {
if (!accessToken || !expires) return;
const token = JSON.stringify({ accessToken, expires });
try {
//使用同步操作
await wx.setStorageSync('token', token);
emitEvent('setTokenOk'); //广播setTokenOk事件
} catch (error) {
console.error('设置 token 时出错:', error);
}
};
/**
* 获取 token
* @returns {Promise<string|null>} - 如果令牌存在且未过期,则返回访问令牌;否则返回 null。
*/
export const getToken = () => {
try {
const value = wx.getStorageSync('token');
if (!value) return Promise.resolve(null);
const userToken = JSON.parse(value);
const nowDate = Date.now();
if (userToken.expires < nowDate) return Promise.resolve(null);
return Promise.resolve(userToken.accessToken);
} catch (error) {
console.error('获取 token 时出错:', error);
return Promise.resolve(null);
}
};
request.js
import { getToken } from './token';
import { registerEventListener } from './event' //注册事件
const BASE_URL = "https://api.com"
//拦截器
async function interceptor(url, data = {}, method = 'GET') {
const token = await getToken();
const whiteUrlList = ['/getWxOpenId']; //此处替换成获取的实际接口
if (whiteUrlList.includes(url)) {
return request(url, data, method);
} else if (token) {
return request(url, data, method, token);
} else {
// 当没有 token 时,等待 setTokenOk 事件触发后重新发起请求
return new Promise(resolve => {
//注册 setTokenOk 事件
registerEventListener('setTokenOk', async () => {
const newToken = await getToken();
resolve(request(url, data, method, newToken));
});
});
}
}
//实际请求
function request(url, data = {}, method = 'GET', token = '') {
return new Promise((resolve, reject) => {
wx.request({
url: BASE_URL + url,
data,
method,
timeout: 5000,
header: {
"x-token": token,
},
success: res => {
resolve(res.data);
},
fail: err => {
reject(err);
}
})
})
}
export const post = (url, data = {}) => interceptor(url, data, 'POST');
App.js
import { setToken, getToken } from "./utils/token"
import { post } from './utils/request'
App({
async onLaunch() {
await this.checkToken()
},
async checkToken() {
const token = await getToken()
if (!token) {
await wx.login({
success: async (res) => {
try {
const { code, data } = await post('/getWxOpenId', { code: res.code })
if (code === 0) {
setToken(data)
}
} catch (error) {
console.log(error);
}
}
})
}
},
})