微信小程序之接口预加载

我正在参加「掘金·启航计划」

之前技术主管让我研究一下微信小程序的请求预加载的方案,但后来因为别的开发任务,预加载这款就搁置下来了。最近开发任务不重,又凑巧看了相关的文章,了解了实现原理,自己写了个轮子,大家请往下看。

小程序官方方案

在小程序文档的性能与体验章节,已经有了请求预加载的方案,就是利用 EventChannel页面通信的方法,在当前页面提前请求下个页面的数据,然后通过EventChannel传递到下个页面,在下个页面开始加载的时候获取到数据,直接渲染。

个人方案

因为自己实现在看到微信接口预加载文档之前,并且按照微信方案同样要解决以下几个痛点

  • 页面A跳页面B,页面B需要预加载的请求我不想在页面A写
  • 如果页面A已经请求了页面B的接口,但接口结果还没回来就跳转到了页面B,这个时候我也不想再次发送请求网络
  • 分包的问题(这个下面详细说)

了解微信的加载流程是实现预加载方案的基础。微信启动加载时候,首先会先加载主包,加载主包会把主包内的页面全部都require(path)进来。在require的过程,page函数会调用,但这时候的页面实例还没有创建,所以路由里是查看不到除主页的其他页面的。

看图片,小程序启动中,在所有的babel和相关资源加载完毕,会创建一个script,script的内容就是加载主内容,第二张图。(个人理解,如有差错,评论请指出,谢谢)

下面我们就可以开始敲代码了,首先我们先约定一下,在需要预加载请求的页面我们要加入额外两个配置,如下:

page({
    path:"xxx/xxx/xxx", //要和app.json中注册的路径一样
    registerPreload(){}//这里写需要预加载的请求
})

然后要重写小程序的page方法

const allPagePreloadMap = {};//所有页面预加载方法的集合
const allPreloadDateMap = {};//所有页面预加载数据的集合

let waitRunPreload; //等待预加载方法注册后执行

function rewritePage (){
    let cPage = Page
   
    Page = function rebuildPage(options){
        if (!options['onLoad']) options['onLoad'] = function () { };
        if (!options['onShow']) options['onShow'] = function () { };
        if (!options['onHide']) options['onHide'] = function () { };
        if (!options['onUnload']) options['onUnload'] = function () { };

        const copyOnLoad = options['onLoad'];
        options['onLoad'] = function(){
            //当前页面是否有注册预加载函数
            if(allPagePreloadMap[this.path]){
                //预加载函数是否执行
                if(allPreloadDateMap[this.path]){
                    this.setData.call(this,{...allPreloadDateMap[this.path]});
                }else{
                    allPagePreloadMap[this.path].call(this,[...arguments]);
                }
            }
            return copyOnLoad.apply(this,[...arguments])
        }

        const copyOnShow = options['onShow'];
        options['onShow'] = function(){
            this.$isBuild = true;
            return copyOnShow.apply(this,[...arguments])
        }

        const copyOnHide= options['onHide'];
        options['onHide'] = function(){
            this.$isBuild = false;
            return copyOnHide.apply(this,[...arguments])
        }

        const copyOnUnload = options['onUnload'];
        options['onUnload'] = function(){
            delete  allPreloadDateMap[this.path];
            return copyOnUnload.apply(this,[...arguments])
        }


        
        // 注册页面的预加载方法
        if(options['registerPreload']){
            // console.log('注册页面的预加载方法',options['path'])
            if(!options['path']) {
                console.error('注册预加载方法必须同时设置页面路径,同路由跳转url')
            }else{
                allPagePreloadMap[options['path']] = options['registerPreload'].bind(options);
                //检查是否已经有该页面的预加载请求调用被缓存
                if(waitRunPreload && waitRunPreload.path == options['path']){
                    waitRunPreload.run(allPagePreloadMap[options['path']])
                    waitRunPreload = null;
                }
            }
        }
        

        //封装setData,建议只在预加载方法中使用,如果页面已经创建,数据直接更新到data,否则存储到缓存中
        options['$setState'] = function(){
            const pageInstance = getCurrentPages()[getCurrentPages().length - 1];
            if(pageInstance.route == this.path){
                allPreloadDateMap[this.path] = {...allPreloadDateMap[this.path],...arguments[0]}
                return pageInstance.setData.apply(pageInstance,[...arguments]);
            }else{
                allPreloadDateMap[this.path] = {...allPreloadDateMap[this.path],...arguments[0]}
            }
        }

        //预加载页面数据
        options['$preload'] = async function(path,data={}){
            allPreloadDateMap[path] = {};
            //在已经调用下个页面预加载请求但该页面所在分包未加载的情况下,缓存path和参数
            if(allPagePreloadMap[path]){
                allPagePreloadMap[path](data);
            }else{
                waitRunPreload = {
                    path,
                    run:function (fn){
                        return fn.call(this,data)
                    }
                }
            }
        }
this.$route('pages/logs/logs',{},true)
        options["$route"] = function (url,data = {},isPreload = false,options = {}){
            if(isPreload){
                allPreloadDateMap[url] = {};
                //在已经调用下个页面预加载请求但该页面所在分包未加载的情况下,缓存path和参数
                if(allPagePreloadMap[url]){
                    allPagePreloadMap[url](data);
                }else{
                    waitRunPreload = {
                        path:url,
                        run:function (fn){
                            return fn.call(this,data)
                        }
                    }
                }
            } 
            url = '/'+url;
            let query = "";
            for(let key in data){
                data[key] + '&' + query;
            }
            url = url + `${query.length?'?'+query:query}`
            if(getCurrentPages().length>9){
                wx.navigateTo({
                    url,
                    ...options
                })
            }else{
                wx.reLaunch({
                    url,
                    ...options
                })
            }
        }
        

        return cPage(options);
    }
    
}

rewritePage()

页面A可以自己决定预加载的时机,可以选择任意时候调用this. p r e l o a d ( 页面 B p a t h , d a t a ) 方法触发页面 B 的网络请求,也可以使用 t h i s . preload(页面Bpath,data)方法触发页面B的网络请求,也可以使用this. preload(页面Bpath,data)方法触发页面B的网络请求,也可以使用this.route(页面Bpath,data,true)方法,在跳转成功后调用。

针对上面提的三个痛点,解决方法如下

痛点1

利用小程序加载过程,缓存所有页面的registerPreload的方法,在页面A调用时候,利用path查找缓存的方法进行调用。

痛点2

因为请求是异步的,可能到了页面B,预加载的请求还没有回来。所以自定义了一个this.$setState方法替换this.setData,并在 registerPreload 方法中使用,在页面B未创建的时候会将Data保存才缓存中,创建完成后,会直接调用this.setData更新到页面。同时在页面B的页面onLoad方法中,会判断 registerPreload 方法是否调用,如果调用,会检查内存中使用是否有请求的数据,有的话会setData到data。

通点3

如果页面B位于分包中,在页面A调用页面B的 registerPreload 方法时候,registerPreload 方法还没有注册到缓存中。解决方案是在调用时候检查缓存中是否已经注册,如果未注册,则缓存调用 registerPreload 的请求,在分包代码加载 页面 registerPreload 注册到缓存时候,检查是否有当前页面的 registerPreload 请求,如果有则立马调用。

源码以上都是个人对微信小程序请求预加载的粗浅理解和应用,欢迎各位大佬评论区留言指导交流。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

华山派developer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值