• uniapp 中实现 onLaunch 异步回调后执行 onLoad 最佳实践


    前言

    好久没写博客了,由于公司业务需要,最近接触uiapp比较多,一直想着输出一些相关的文章。正好最近时间富余,有机会来一波输出了。

    问题描述

    在使用 uni-app 开发项目时,会遇到需要在 onLaunch 中请求接口返回结果,并且此结果在项目各个页面的 onLoad 中都有可能使用到的需求,比如微信小程序在 onLaunch 中进行登录后取得 openid 并获得 token,项目各页面需要带上该 token 请求其他接口。

    问题原因

    在onLaunch 中的请求是异步的,也就是说在执行 onLaunch 后页面 onLoad 就开始执行了,而不会等待 onLaunch 异步返回数据后再执行,这就导致了页面无法拿到 onLaunch 中异步获取的数据。

    解决问题

    知道问题原因之后,解决起来就容易了。作为资深白嫖党,先是搜索了相关资料,发现了下面的解决方案。

    解决方案一

    既然在onLaunch中请求是异步的原因导致这个问题,那改成同步的不就行了,这里利用Promise来解决这个问题。步骤如下。

    步骤一

    在 main.js 中增加如下代码:

    Vue.prototype.$onLaunched = new Promise(resolve => {
        Vue.prototype.$isResolve = resolve
    })
    

    步骤二

    在 App.vue 的 onLaunch 中增加代码 this.$isResolve(),具体如下:

    onLaunch () {
        uni.login({
            provider: 'weixin',
            success: loginRes => {
                login({ // 该接口为我们自己写的获取 openid/token 的接口,请替换成自己的
                    code: loginRes.code
                }).then(res => {
                    try {
                        console.info(res.data.token)
                        uni.setStorageSync('token', res.data.token)
                        this.$isResolve()
                    } catch (e) {
                        console.error(e)
                    }
                })
            }
        })
    }
    

    步骤三

    在页面 onLoad 中增加代码 await this.$onLaunched,具体如下:

    async onLoad(option) {
        await this.$onLaunched
    
        let token = ''
        try {
            token = uni.getStorageSync('token')
        } catch(e) {
            console.error(e)
        }
    
        // 下面就可以使用 token 调用其他相关接口
    }
    

    有了这个解决方案,我就开始在实际项目中是用来了。但随着项目的复杂度增加,发现这个方案使用起来有一些弊端。每个页面都需要在 onLoad 中增加代码代码也太烦人了。

    有没有更优雅的方案呢?继续查找资料,有个解决方案是定制一个页面钩子,然后注册全局的异步任务,定义钩子的触发条件,满足条件时即可自动执行页面里相关的钩子。相关方案见参考资料2。

    但这个方案我也不太满意,仍然需要在页面添加一些函数去响应请求。后面突然想到,可以监听路由变动,在路由跳转之前完成请求。

    解决方案二(推荐)

    正好项目中用到了uni-simple-router插件,提供了全局前置守卫事件beforeEach,其本质是代理了所有的生命周期,让生命周期更加可控,这样就可以很好的解决我们面临的问题了。步骤如下:

    步骤一

    在 route.js 增加如下代码:

    // 登录(可放在公共函数里面)
    const login = () => {
        return new Promise(function(resolve, reject) {
            uni.login({
                provider: 'weixin',
                success: loginRes => {
                    login({ // 该接口为我们自己写的获取 openid/token 的接口,请替换成自己的
                        code: loginRes.code
                    }).then(res => {
                        resolve(res);
                    }, err => {
                        reject(err);
                    })
                },
                fail: err => {
                    reject(err);
                }
            });
        });
    }
    
    // 获取token(可放在公共函数里面)
    const getToken = () => {
        let token = ''
        try {
            token = uni.getStorageSync('token')
        } catch(e) {
            console.error(e)
        }
        return token;
    }
    
    // 是否登录
    let hasLogin = false;
    router.beforeEach(async (to, from, next) => {
        // 首次进来,没有登录并且token不存在先请求数据
        if(!hasLogin&&!getToken()){
            const res = await login();
            try {
                console.info(res.data.token)
                uni.setStorageSync('token', res.data.token)
                hasLogin = true
            } catch (e) {
                console.error(e)
            }
        }
        next()
    })
    

    步骤二

    在页面 onLoad 中直接就可以获取 token 并使用,具体如下:

    onLoad(option) {
        let token = ''
        try {
            token = uni.getStorageSync('token')
        } catch(e) {
            console.error(e)
        }
    
        // 下面就可以使用 token 调用其他相关接口
    }
    

    这个解决方案就灵活很多,只需要在 route.js 中写入代码,其他任意地方都可以调用。不用担心新增页面忘记相关方法的引入,更加灵活自由。

    由于这个解决方案基于uni-simple-router插件,在使用前需要引入这个插件。如果不想引入插件,可以自行实现代码生命周期功能。

    PS:大家有更好的解决方案,欢迎在评论区交流。

    参考资料

    1. uni-app 中利用 Promise 实现 onLaunch 异步回调后执行 onLoad
    2. 小程序app.onLaunch与page.onLoad异步问题的最佳实践
    3. 代理生命周期 | uni-simple-router
  • 相关阅读:
    [luogu p2482] [SDOI2010]猪国杀
    [luogu p2296] 寻找道路
    左右布局(备用复制)
    导出Excel
    流式布局 及 媒体查询
    echarts设置(持续更新)
    解决Vue中watch首次进入路由不触发的问题
    Math.random
    Vue的拖拽
    使的dialog上下左右居中(弹框居中)
  • 原文地址:https://www.cnblogs.com/oyjt/p/16302276.html
Copyright © 2020-2023  润新知