评论

小程序app.onLaunch与page.onLoad异步问题的最佳实践

app.onLaunch与page.onLoad异步问题终极解决方案

场景:

在小程序中大家应该都有这样的场景,在onLaunch里用wx.login静默登录拿到code,再用code去发送请求获取token、用户信息等,整个过程都是异步的,然后我们在业务页面里onLoad去用的时候异步请求还没回来,导致没拿到想要的数据,以往要么监听是否拿到,要么自己封装一套回调,总之都挺麻烦,每个页面都要写一堆无关当前页面的逻辑。

直接上终极解决方案,公司内部已接入两年很稳定:

1.可完美解决异步问题

2.不污染原生生命周期,与onLoad等钩子共存

3.使用方便

4.可灵活定制异步钩子

5.采用监听模式实现,接入无需修改以前相关逻辑

6.支持各种小程序和vue架构

。。。

//为了简洁明了的展示使用场景,以下有部分是伪代码,请勿直接粘贴使用,具体使用代码看Github文档
//app.js
//globalData提出来声明
let globalData = {
  // 是否已拿到token
  token: '',
  // 用户信息
  userInfo: {
    userId: '',
    head: ''
  }
}
//注册自定义钩子
import CustomHook from 'spa-custom-hooks';
CustomHook.install({
 'Login':{
    name:'Login',
    watchKey: 'token',
    onUpdate(token){
      //有token则触发此钩子
      return !!token;
    }
  },
 'User':{
    name:'User',
    watchKey: 'userInfo',
    onUpdate(user){
      //获取到userinfo里的userId则触发此钩子
      return !!user.userId;
    }
  }
}, globalData)
// 正常走初始化逻辑
App({
  globalData,
  onLaunch() {
      //发起异步登录拿token
      login((token)=>{
          this.globalData.token = token
          //使用token拿用户信息
          getUser((user)=>{
             this.globalData.user = user
          })
     })
   }
})
//关键点来了
//Page.js,业务页面使用
Page({
  onLoadLogin() {
       //拿到token啦,可以使用token发起请求了
       const token = getApp().globalData.token
    },

  onLoadUser() {
       //拿到用户信息啦
       const userInfo = getApp().globalData.userInfo
    },

  onReadyUser() {
       //页面初次渲染完毕 && 拿到用户信息,可以把头像渲染在canvas上面啦
       const userInfo = getApp().globalData.userInfo
       // 获取canvas上下文
       const ctx = getCanvasContext2d()
       ctx.drawImage(userInfo.head,0,0,100,100)
    },

  onShowUser() {
       //页面每次显示 && 拿到用户信息,我要在页面每次显示的时候根据userInfo走不同的逻辑
       const userInfo = getApp().globalData.userInfo
       switch(userInfo.sex){
         case 0:
           // 走女生逻辑
           break
         case 1:
           // 走男生逻辑
           break
       }
    }
})


具体文档和Demo见↓

Github:https://github.com/1977474741/spa-custom-hooks

祝大家用的愉快,记得star哦

最后一次编辑于  2023-04-23  
点赞 65
收藏
评论

52 个评论

  • 人艰不拆
    人艰不拆
    2024-01-19

    这个github链接打不开

    2024-01-19
    赞同
    回复 1
    • Q.ZHANG
      Q.ZHANG
      2024-01-22
      。。。用梯子试试
      2024-01-22
      回复
  • K
    K
    2024-01-15

    请问uniapp+vue3 可以用?

    2024-01-15
    赞同
    回复 4
    • Q.ZHANG
      Q.ZHANG
      2024-01-15
      vue3不支持
      2024-01-15
      回复
    • K
      K
      2024-01-17回复Q.ZHANG
      有考虑?
      2024-01-17
      回复
    • Q.ZHANG
      Q.ZHANG
      2024-01-17回复K
      还不清楚目前vue3在类似场景下是怎么处理的,得看有没有必要,vue2是因为借助选项式API的能力,使用比较方便,vue3要想要同样的能力就必须得多一个调用Api的步骤了,跟单纯的回调类似,就直接在原生生命周期内监听一下回调,跟封装成库差不了太多,没有太大价值了。我没在业务中用过vue3,你可以说一下你相关场景的经验
      2024-01-17
      回复
    • K
      K
      2024-01-18回复Q.ZHANG
      uniapp+vue3 目前登录我是有个中转页 中转页登录成功了再跳到相应的页面;不然的话app.js登录还没拿到token   其他页面就已经请求了
      2024-01-18
      回复
  • 别叫我小小
    别叫我小小
    2023-10-25

    现在支持原生小程序了吗?

    2023-10-25
    赞同
    回复 2
    • Q.ZHANG
      Q.ZHANG
      2023-10-27
      一直都支持原生小程序
      2023-10-27
      回复
    • Q.ZHANG
      Q.ZHANG
      2024-02-29
      安装下最新版本,支持所有原生小程序的组件,包括lifetimes和pageLifetimes
      2024-02-29
      回复
  • 惬意
    惬意
    2023-10-25

    系统提供的这个怎么样

    app.userInfoReadyCallback


    2023-10-25
    赞同
    回复 3
    • Q.ZHANG
      Q.ZHANG
      2023-10-25
      有没有文档?我没找到,搜了下大概意思是异步获取微信用户信息的回调,仅此而已,就是个基础的异步api,就像你正常发的请求的回调一样,没什么好说的。
      2023-10-25
      回复
    • 惬意
      惬意
      2023-11-27回复Q.ZHANG
      这个文档还真没找到,这个是好几年前做小程序的时候在文档中看到的,不知道怎么现在找不到了。当时也是为了解决app.js里异步获取到用户信息,然后在自己的页面实现异步回调。看到这个问题特意去翻的好几年前的代码。
      2023-11-27
      回复
    • Q.ZHANG
      Q.ZHANG
      2023-11-27回复惬意
      啊,这样吗?这样看起来不是系统的api,这就是纯js手工实现的一段代码,onLoad先走,onLoad里往App上挂了个userInfoReadyCallback函数引用,App异步任务回来后再执行这个函数,属于这篇文章里的“注册回调”的方式。https://developers.weixin.qq.com/community/develop/article/doc/000eae817f8828bf905d767cd5f413
      2023-11-27
      1
      回复
  • y@ng
    y@ng
    2023-10-08

    分享一下我碰到的问题;已解决

    我的问题是第一次编译的时候,userInfo对象数据更新,无法触发Page页面onLoadXX(XX代表监听函数的名字),第二次确可以。

    我的猜想说一下,首先这个钩子函数应该是通过object.defineProperty进行监听对象的。当通过直接赋值的形式操作时(改变源对象的内存地址),那么钩子函数就监听不到了,所以就导致我这边onLoadXX(XX代表监听函数的名字)执行不了。下面是一个简单的demo

    let number = 20;
            let obj = {a:1};
            let obj1 = {a:2};
            
            Object.defineProperty(obj,"b",{
                get(){
                    console.log("有人读取了b属性")
                    return number
                },
                set(val){
                    number = val
                }
            })
            obj = obj1;
            obj.b = 30;
            console.log(obj,number);// {a:2,b:30} (不再是响应式了) number=>20
    
    

    

    改善方法

    obj = obj1; 
    //改为对对象进行合并,保留源对象的内存地址
    obj = Object.assign(obj,obj1);
    obj.b = 30;
    console.log(obj,number);// {a:2,b:30} (不再是响应式了) number=>30
    


    希望大家碰到这个问题能快速解决,反正我花了不少时间解决这个问题。如果条件有限的 作者可以加个判断,判断当前对象内存地址是否已经变化,然后告诉开发者。

    2023-10-08
    赞同
    回复 1
    • Q.ZHANG
      Q.ZHANG
      2023-10-08
      直接对watchKey直接赋值是可以监听到的,你是说对整个globalData重新赋值吗?改变了原有globalData的引用?还是
      2023-10-08
      回复
  • 流年
    流年
    2023-09-17

    uniapp用不起来嘛


    2023-09-17
    赞同
    回复 1
    • Q.ZHANG
      Q.ZHANG
      2023-09-18
      能啊,怎么用不起来?是vue2吗
      2023-09-18
      回复
  • 逸影之城
    逸影之城
    2023-07-11

    钉钉小程序调用为啥报错不能使用吗

    2023-07-11
    赞同
    回复 2
    • Q.ZHANG
      Q.ZHANG
      2023-07-12
      把它拿出来放项目里,用相对路径引用,编译配置的问题,改天我整个钉钉小程序模板看能否复现
      2023-07-12
      回复
    • Q.ZHANG
      Q.ZHANG
      2023-08-07
      抱歉,找到问题了,阿里系小程序确实,目前已经修复了,你安装一下最新版本
      2023-08-07
      回复
  • 哈尔的移动城堡
    哈尔的移动城堡
    2023-06-24

    大哥,请问在vue3中怎么使用该钩子函数?



    2023-06-24
    赞同
    回复 2
  • king
    king
    2023-06-09

    我试了,在微信开发工具上,切换页面时Page下的onShowLogin有时候执行不了,偶发性的,这是什么问题。


    2023-06-09
    赞同
    回复 1
    • Q.ZHANG
      Q.ZHANG
      2023-06-09
      还从来没出现过这个问题。。确定排查了不是业务代码问题的话,你加我微信ZZZQQQOOO
      2023-06-09
      回复
  • 周生生
    周生生
    2023-05-25

    直接上代码

    // util.js
    let onLaunchedCallback
    const onLaunchedPromise = new Promise(resolve => {
      onLaunchedCallback = resolve
    })
    // App调用
    export const setLaunched = success => onLaunchedCallback(success)
    // 页面调用
    export const onLaunched = () => onLaunchedPromise
    
    // App.vue
    import { setLaunched } from "./util.js"
    export default {
      async onLaunch() {
        // 发起登录请求
        let success = await this.login()
    
        // 登录成功后调用回调(通知promise resolve)
        setLaunched(success)
      },
      methods: {
        async login() {
          // ...省略登录逻辑代码
          return true
        },
      },
    }
    
    // page
    import { onLaunched } from "./util.js"
    export default {
      name: "HomePage",
      async onLoad() {
        await onLaunched()
    
        // this.fetchData()
      },
    }
    
    2023-05-25
    赞同
    回复

正在加载...

登录 后发表内容