问题描述
我们在用 uni-app 开发前端项目时,会遇到需要在 onLaunch 中请求接口返回结果,并且此结果在项目各个页面的 onLoad 中都有可能使用到的需求,比如微信小程序在 onLaunch 中进行登录后取得 openid 并获得 token,项目各页面需要带上该 token 请求其他接口。但是,onLaunch 中的请求是异步的,也就是说在执行 onLaunch 后页面 onLoad 就开始执行了,而不会等待 onLaunch 异步返回数据后再执行,这就导致了页面无法拿到 onLaunch 中异步获取的数据。
解决方案
步骤1
在 main.js 中增加如下代码:
Vue.prototype.$onLaunched = new Promise(resolve => {
Vue.prototype.$isResolve = resolve
})
步骤2
在 App.vue 的 onLaunch
中增加代码 this.$isResolve()
,具体如下:
onLaunch () {
// #ifndef H5
uni.login({
success: loginRes => {
// #ifdef MP-WEIXIN
login({ // 该接口为我们自己写的获取 openid/token 的接口,请替换成自己的
appId: 'wx1234567890',
code: loginRes.code
}).then(res => {
try {
console.info(res.object.token)
uni.setStorageSync('mcToken', res.object.token)
this.$isResolve()
} catch (e) {
console.error(e)
}
})
// #endif
}
})
// #endif
}
步骤3
在页面 onLoad
中增加代码 await this.$onLaunched
,具体如下:
async onLoad(option) {
await this.$onLaunched
let token = ''
try {
token = uni.getStorageSync('mcToken')
} catch(e) {
console.error(e)
}
// 下面就可以使用 token 调用其他相关接口
}
版权属于:瞭月
本文链接:https://www.lervor.com/archives/128/
版权声明:本文为瞭月原创文章,转载请附上原文出处链接和本声明。
34 条评论
这个还是挺巧妙的
可是这样会导致onLoad获取不到链接上带的参数,onLoad(options),options是空的。
不影响,原本怎么获取的,现在还是怎么获取的。确认下看看是不是链接参数传递有误?
感谢大佬,方法很巧,代码也不多
谢谢大佬的方案,这里我提一点,如果需要登录的话,没啥问题,但是已经登录过了,需要在onLaunch中,设置一个延迟调用的函数,才能完成整个流程,不知道我理解是否正确。
let loginInfo = uni.getStorageSync('loginInfo') if (loginInfo && loginInfo.token) { setTimeout(()=>{ _this.$isResolve(true) }, 500) return }比如onLaunch中
// token 存在时,不需要发起login
因为整个生命周期 onLaunch 只会执行一次,建议每次进入 onLaunch 都进行重新登录。如果确实不想重新登录,可通过判断 token 是否存在直接执行 $isResolve,可以不使用定时器。
但是直接执行 $isResolve,会报错,因为$isResolve这变量是需要具体页面执行$onLaunched才会生成。
在token存在的情况下,onLaunch先执行完毕,具体的页面刚刚调用$onLaunched生成$isResolve。
因为时序问题,导致onLaunch在token存在时,直接执行$isResolve出错为undefined,所以猜想到这个延迟执行$isResolve
不知道是不是哪里出了问题,onLaunch和页面的onLoad并列执行,非先后顺序。
你这会不会是哪里写错,三要素不能少,看看有没有漏掉哪个环节。
1、main.js 中 $isResolve 的定义赋值
2、App.vue 中 $isResolve() 的调用
3、onLoad 中通过 async 和 await 实现同步,且调用 await this.$onLaunched
如果还报 $isResolve 为 undefined,可能是 this 的作用域有问题
这个只是在有返回的情况下的处理,如果接口返回错误没有处理?
这个只是简单做个示例,异常是可以处理的,比如在onLaunch中请求接口异常的时候直接跳转错误页面,错误页面不加入:await this.$onLaunched。也可以通过在onLaunch中请求接口异常的地方加入this.$isResolve(false),在页面onLoad中通过const result = await this.$onLaunched来接收onLaunch中传过来的值进行后续业务
比如成功的时候this.$isResolve(true),异常或失败的时候this.$isResolve(false),const result = await this.$onLaunched的结果是true或false来判断onLaunch中的操作是否成功,如果没有特殊业务要求,建议直接第一种方便。
main.js 中
Vue.prototype.$isResolve = resolveVue.prototype.$onlaunched = new Promise (resolve=>{
})
app.vue中
success:(res)=> { // 获取code if(res.code){ if(false){ uni.setStorageSync('isbinding',false) }else{ uni.setStorageSync('isbinding',true) } this.$isResolve(); console.log(res) }else{ console.log('登录失败!',res.errMsg) }// 微信
wx.login({
}
})
index.vue 中
data(){
isbinding:flase
}
async onload(){
await that.$onlaunched
that.isbinding = uni.getStorageSync('isbinding')
}
这样写会有一个不到一秒的抖动 that.isbinding的值会先为false 再变为true
而根据这个变量判断页面是否显示 刚开始半秒的时间不显示,然后才显示
大佬,这样有解决办法吗?
你在index.vue里定义了isbinding:flase,然后又根据这个变量判断页面是否显示页面,而且渲染的时候又在等待wx.login结果,因此等待期间肯定是不显示一段时间有结果了才显示,建议在index.vue中加入一个loading或者等待图(有点像小程序的启动页面),isbinding为false的时候显示等待图,为true的时候正常显示界面元素
isbinding为true显示的是遮罩页面,为false是正常页面
是这样的: 当isbinding为false时正常显示页面,为true时在原页面的上边弹出一个遮罩不让点击那个页面,不能添加loading和等待图的,因为它是有为false的情况的
这个是正常现象,isbinding默认false,刚进去就不会显示遮罩层,在等待登录后才会变true,有两种解决方案:1、isbinding默认值改成true,先显示遮罩层,登录后满足false的时候去掉。2、如果不想让用户看到遮罩层的切换状态,还是建议加上等待图(可全屏),loadingImg=true的时候显示等待图,false时去掉等待图,显示正常页面元素。
data() { isbinding: flase, loadingImg: ture }, async onload() { await this.$onlaunched this.isbinding = uni.getStorageSync('isbinding') this.loadingImg = false }大佬,带我飞
必须顶一个,太赞了!(☆ω☆)
H5有用吗
可以用
一样的
找了好久,见过最简洁的实现方式
一起学习,共同进步
我按照上面的实例写的为什么报错啊Cannot read property '$isResolve' of undefined
main.js 的代码写前面
不知道你是怎么写的?哪一行报错?this.$isResolve有没有放在vue文件里?
有点强,但是我想知道,难道我很多个页面都需要这种,那就需要每个页面都写上await this.$onLaunched吗?
可以使用混入,请参考:https://www.lervor.com/archives/141/,如使用全局混入,await this.$onLaunched 后再调用页面中的特定方法(如initPage),可以根据实际情况选择使用方式。
不可以呀
我这边用是可以的,Mixins 文件里面 onLoad(option) {if (this.initPage) {await this.$onLaunched; this.initPage(option);}},页面中定义函数 async initPage(option) {this.id = option.id; 接下去写http请求}
这都行,牛逼