微信小程序静默登录踩坑:首页接口比登录接口快?4种方案完美解决
问题:
在app.vue 的 onLaunch 中做静默登录,同时首页pages/index/index onLoad 中请求核心业务接口,但经常出现 首页接口比登录接口先完后的情况----导致首页接口因缺少登录态 token 请求失败,用户进入首页后看不到数据
问题本质:
微信小程序中,app.vue 的onLaunch 和首页的onLoad 是并行执行的, 而非顺序执行,静默登录是异步操作(调用wx.login,后端接口 ),需要一定时间,而首页onLoad 中的接口请求也是异步操作,且执行时机可能比登录接口更快,此时本地还没有存储token,首页接口携带空token 请求,自然会失败
方案:
方案1:最简单直接——首页 onLoad 中延迟请求(适合快速调试/简单项目)
方案2: 利用 Promise 等待登录完成(推荐,适配大多数项目)
-
步骤1:app.vue 封装登录 Promise
-
步骤2:首页等待登录 Promise 完成
// pages/index/index.vue 优化后 Page({ async onLoad() { // 等待app.vue的登录Promise完成 const loginSuccess = await getApp().loginPromise; if (loginSuccess) { // 登录成功,请求首页接口 this.getHomeData(); } else { // 登录失败,提示用户 wx.showToast({ title: '登录失败,请重试', icon: 'none' }); } }, methods: { async getHomeData() { const token = wx.getStorageSync('token'); const res = await wx.request({ url: 'https://xxx.com/api/home', method: 'GET', header: { 'Authorization': `Bearer ${token}` } }); // 后续逻辑... } } })
方案3: uniapp 小程序(Vue3) 专属方案 —— 基于全局Promise 控制onLaunch 与onLoad 执行顺序(main.js + app.vue 配置)
核心是在main.js 全局挂载Promise实例,app.vue的onLaunch 完成异步登录后触发Promise 的 resolve,所有页面onLoad 通过await 等待该Promise,确保登录完成后再执行接口请求,与方案2的局部Promise不同,它全局适配所有页面,贴合Vue3语法(通过getCurrentInstance 获取 proxy访问全局属性), 仅适配uni-app Vue3,不兼容Vue2
1. 修改 main.js,全局挂载 Promise(关键配置)
// main.js(uni-app 入口文件,Vue3版本)
import { createSSRApp } from 'vue'
import App from './App.vue'
export function createApp() {
const app = createSSRApp(App)
// 核心:全局挂载Promise实例,用于控制onLaunch与onLoad的执行顺序
app.config.globalProperties.$onLaunched = new Promise(resolve => {
// 挂载resolve方法,供app.vue的onLaunch调用,标记onLaunch执行完成
app.config.globalProperties.$isResolve = resolve
})
return { app }
}
2.修改 app.vue(Vue3 语法),onLaunch 中触发 resolve
// app.vue(uni-app,Vue3 语法,setup 语法糖)
<script setup>
import { getCurrentInstance, onLaunch } from "vue"
// 获取全局实例 proxy(Vue3 中获取全局属性的方式)
const { proxy } = getCurrentInstance()
// 静默登录方法(异步)
const silentLogin = async () => {
try {
// 1. 获取微信/uni 登录 code(uni 多端兼容写法)
const { code } = await uni.login({ provider: 'weixin' })
// 2. 调用后端静默登录接口,获取 token
const [err, res] = await uni.request({
url: 'https://xxx.com/api/silentLogin',
method: 'POST',
data: { code }
})
// 3. 存储 token(uni 多端兼容存储)
if (res?.data?.code === 200) {
uni.setStorageSync('token', res.data.data.token)
}
} catch (err) {
console.error('静默登录失败:', err)
uni.showToast({ title: '登录失败,请重试', icon: 'none' })
}
}
// onLaunch 生命周期(Vue3 setup 语法写法)
onLaunch(async () => {
// 1. 执行异步静默登录
await silentLogin()
// 2. 关键:登录完成后,触发全局 Promise resolve
// 通知所有页面:onLaunch 已执行完毕,可以执行 onLoad 了
proxy.$isResolve()
})
</script>
3. 首页 (Vue3语法), onLoad 中等待全局 Promise 完成
首页onLoad中,先通过await 等待全局挂载的$onLaunched Promise完成(即onLaunch中所有异步操作执行完毕),再请求业务接口,确保能获取到有效的登录态(token)
// pages/index/index.vue(uni-app,Vue3 语法,setup 语法糖)
<script setup>
import { getCurrentInstance, onLoad } from "vue"
const { proxy } = getCurrentInstance()
// 首页核心接口请求
const getHomeData = async () => {
// 获取存储的 token
const token = uni.getStorageSync('token')
// 兜底:防止登录失败导致 token 为空
if (!token) {
uni.showToast({ title: '登录失败,无法加载数据', icon: 'none' })
return
}
// 调用首页接口(uni 多端兼容请求)
const [err, res] = await uni.request({
url: 'https://xxx.com/api/home',
method: 'GET',
header: {
'Authorization': `Bearer ${token}`
}
})
if (res?.data?.code === 200) {
// 处理首页数据
console.log('首页数据:', res.data.data)
} else {
uni.showToast({ title: '数据加载失败', icon: 'none' })
}
}
// 页面 onLoad 生命周期
onLoad(async () => {
// 关键:等待全局 Promise 完成(等待 onLaunch 执行完毕)
await proxy.$onLaunched
// 此时 onLaunch 已执行完,登录态已生成,请求首页接口
getHomeData()
})
</script>
关键说明
- 核心原理: main.js 中挂载的$onLaunched 是一个未resolve 的Promise,页面onLoad中await该Promise 时,会阻塞onLoad的后续逻辑; 直到app.vue的onLaunch中执行proxy.$isResolve(),Promise状态变为完成,页面onLoad 才会继续执行
- Vue3 语法重点: getCurrentInstance( ) 用于获取当前组件实例,proxy 是实例的代理对象,通过proxy可以访问到app.config.globalProperties 上挂载的全局属性($onLaunched,$isResolve)
- 与方案2的区别: 方案2是在app.vue 中定义局部Promise (loginPromise), 而该方案是通过main.js 全局挂载Promise,适配所有页面,无需在每个页面单独关联app 的Promise,更适合页面的uni小程序项目
方案4: 最优雅——全局拦截器 + 登录状态管理(适合中大型项目)
核心逻辑: 封装全局请求拦截器,所有接口请求前先判断是否有token; 若无token,等待登录完成后再发起请求; 若登录失败,统一处理(如跳转登录页),这种方式适合多页面,多接口的中大型项目,可统一管理登录态和接口请求
-
封装全局请求(utils/request.js)
-
(若用方案2,保留 loginPromise;若用方案3,保留 onLaunch 返回 Promise,拦截器均可适配)
-
首页使用全局请求
总结 :
- 简单项目/快速调试 -> 方案1
- 大多数常规项目 - > 方案2
- uni-app Vue3项目 -> 方案3
- 中大型多页面项目 => 方案4 : 统一管理,可扩展性强,适合长期维护
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)