IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 微信小程序之接口预加载 -> 正文阅读

[移动开发]微信小程序之接口预加载

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

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

小程序官方方案

在小程序文档的性能与体验章节,已经有了请求预加载的方案,就是利用 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 请求,如果有则立马调用。

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

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-11-05 00:39:30  更:2022-11-05 00:39:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 20:32:53-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码