chender
chender
  • 发布:2015-07-12 19:18
  • 更新:2017-03-20 00:35
  • 阅读:12873

分享一个js,如何更方便地进行跨webview的调用

分类:MUI

跨webview的调用目前可以通过evalJs和事件机制来实现,但是如果涉及到回调的时候,又得反向的使用evalJS或事件机制,虽也能实现功能,但显得比较臃肿;如果遇到回调时需要使用闭包资源时,那基本上就没辙了;
所以我对evalJS进行了简单的封装,代码很简单,不到一百行,可以实现带回调的跨webView调用;但目前只支持传递一个JSON类型的参数,如果需要传递多个参数的时候,可把多个参数封装到一个JSON对象中进行传递;
样例代码如下
主页面:

  mui.init();  
            mui.plusReady(function() {  
                var sub = plus.webview.create("sub.html", "新窗口", {}, {});  
                sub.addEventListener("loaded", function() {  
                    VG.method(sub, "call", {//调用子页面的call方法  
                        a: "000",  
                        b: "001"  
                    }, function(back) {  
                        alert("父页面接收到回调参数:" + JSON.stringify(back));  
                    });  
                });  
            });  

子页面:

mui.init();   
        function call(param,callback){  
            alert("子页面接收到值:"+JSON.stringify(param));  
            callback({a:"123",b:"456"});  
        }  

源码:

/**  
 * 创建于:2015-5-16<br>  
 * 支持回调的跨webview方法调用  
 * @author chender  
 * @version 0.9  
 * TODO 支持传递多个参数  
 */  
(function($) {  
    window.VG = new function() {  
        var callbackPool = {};  
        var callbackKey = "callbackKey_";  
        var callbackIndex = 0;  
        var curViewId = null;  
        /**   
         * @description 向回调池里面新增一个callback函数,并返回索引值  
         * @param {Function} callback=[] 回调函数  
         * @return {String} 该回调函数的索引值,可通过该值获取到存放的回调函数  
         */  
        this.pushCallback = function(callback) {  
                var key = callbackKey + (callbackIndex++);  
                callbackPool[key] = callback;  
                return key;  
            }  
            /**   
             * @description 通过索引值从回调池里面取出一个callback函数  
             * @param {String} key=[] 回调函数索引值  
             * @return {Function} 存放的回调函数  
             */  
        this.pullCallback = function(key) {  
                var back = callbackPool[key];  
                delete callbackPool[key];  
                return back;  
            }  
            /**  
             * @description 调用其他webview的方法,当前webview必须设置id值(方便回调)  
             * @param {Webview} webview=[webview] 需要调用方法的webview  
             * @param {String} methodName=[methodName] 需要调用的方法的名称  
             * @param {JSON} param=[param] 调用时传递的参数,为json格式  
             * @param {Function} callback=[callback] 回调函数  
             */  
        this.method = function(webview, methodName, param, callback) {  
            curViewId = curViewId ? curViewId : (curViewId = plus.webview.currentWebview().id);  
            if (curViewId == null) {  
                var msg = "该webview没设置id,无法使用该功能";  
                console.log(msg);  
                throw new Error(msg);  
            }  
            if (webview == null) {  
                var msg = "找不到对应的webview,webviewDd:" + webviewId;  
                console.log(msg);  
                throw new Error(msg);  
            }  
            var callbackKey = null;  
            if (callback) {  
                callbackKey = this.pushCallback(callback);  
            }  
            evalJS(webview, "VG._methodCalled", curViewId, methodName, JSON.stringify(param), callbackKey);  
        }  

        this._methodCalled = function(viewId, methodName, param, callbackKey) {  
            //TODO 多参数如何实现  
            var method = window.eval(methodName);  
            method(JSON.parse(param), callbackKey && function(backParam) {  
                evalJS(plus.webview.getWebviewById(viewId), "VG._methodBacked", callbackKey, backParam && JSON.stringify(backParam) || "");  
            });  
        }  
        this._methodBacked = function(callbackKey, backParam) {  
            var callback = this.pullCallback(callbackKey);  
            callback(backParam && JSON.parse(backParam));  
        }  

        function evalJS(webview, methodName) {  
            var params = "";  
            if (arguments.length > 2) {  
                for (var i = 2; i < arguments.length; i++) {  
                    if (arguments[i] === null || arguments[i] === undefined) {  
                        params += "null,";  
                    } else {  
                        params += "'" + arguments[i] + "',";  
                    }  
                }  
                params = params.substring(0, params.length - 1);  
            }  
            webview.evalJS(methodName + "(" + params + ")");  
        }  
    }  
})(window)

个人觉得这个功能比较实用,性能什么的也不会有什么问题,看官方能不能采纳,纳入框架的范畴;

2015-07-12 19:18 负责人:无 分享
已邀请:
闪闪

闪闪

核心还是父webview给子webview来了个evalJS,把父webview的id、参数、回调函数传过去,让子webview执行完对应方法后反向再给父webview来个evalJS,把回调函数给执行了。
把父子webview互相evalJS封装了一下。
在父webview维护了一个callbackPool,什么样的场景需要一个回调函数池?

wanZ

wanZ

这个是用了js类的形式,把所有的webview都放入到一个“堆栈里边”。
创意很好,希望可以有更完善的列表管理功能,现在只有push没有pop

可以仿照c++的vector来加强一下

最后还是谢谢分享新的思路

  • chender (作者)

    有pop,pullCallback方法里面的那一句 delete callbackPool[key],就相当于是出栈的操作了

    2015-07-12 20:54

  • wanZ

    nice,就是相当于覆盖吧

    2015-07-12 21:00

  • 闪闪

    你看明白代码了吗?哪里把所有webview放入堆栈了。。。

    2015-11-30 09:56

朋也

朋也 - https://tomoya92.github.io

为啥不把代码格式化一下呢?这样看着好乱

  • chender (作者)

    格式化了显示出来还是这样的,拷出来用吧

    2015-07-14 11:59

朋也

朋也 - https://tomoya92.github.io

@chender

这个编辑器上面有个格式化代码的按钮

也可以手动写到编辑器里

比如:

function abc(){  
    alert(123);  
}

写法是:

如果就是一行代码,比如:
var a = 1;

写法是:

hum

hum

如果可以直接调用各个页面的函数,就比较好用了。而不是现在这样需要使用evalJS来解决,真心的蛋疼的问题。

  • chender (作者)

    跨webview的想直接调用时不可能的,用我这种方式和直接调方便度已经差不多了,基本上没有优化的余地了

    2015-07-18 11:13

guyskk

guyskk - https://plant.onebizlab.top

欢迎看一下一种更简单,更灵活的实现
http://ask.dcloud.net.cn/question/7694?item_id=12906

  • chender (作者)

    实现的思路都差不多,但算不上更灵活吧,另外给你提了几点意见,可以优化一下

    2015-07-18 23:14

hum

hum

其实大家做的都比较麻烦,最好的方法还是官方出一个内置的共享内存的机制,这个完全是可以实现的。

  • guyskk

    原理都是通过wobj.evalJS( js )跨webview执行代码

    2015-07-19 18:14

chender

chender (作者) - 与人为善

这个谈何容易,想一下,不同的web页面能共享内存共享变量,带来的问题比解决的问题还要多,不现实

hum

hum

其实我觉得这个东西比较难用的地方就在于evalJS回调的限制

比如有两个页面a.html, b.html
a.html 里头有个函数
function test(){
return new Date()
}

如果b.html里头可以直接执行
var test = plus.webview.getWebviewById('a.html').test();

那这个框架就牛逼闪闪了。

chender

chender (作者) - 与人为善

把我写这个东西集成到plus里面就能大概实现了,但是还是不能webview.foo()这样调,因为a页面里面根本不知道b页面里有什么方法,所以在a页面里直接webview.bFoo()是不太好实现了,如果把方法名bFoo当参数传递进行调用的话,就好实现得多了

wenju

wenju - https://www.mescroll.com -- 精致的下拉刷新和上拉加载组件

官方的 mui.fire 不是挺好用的吗?

  • chender (作者)

    不能带回调函数

    2015-07-22 10:16

  • 立扬

    不是预加载的不能使用fire,现载的吧如果不做设置只是第一次创建传值有效,再次打开其实就跟预加载效果一样了,传值和执行比较麻烦

    2015-12-04 16:45

levy

levy

这个只支持打包app的机制吧?微信的wap版是否可以用?

  • chender (作者)

    这是html5+环境上的一个跨webview调用的工具,微信上的app应该是不行

    2015-07-22 21:45

  • levy

    回复 chender:了解了,谢谢

    2015-07-28 13:11

jolynekujo

jolynekujo

请问一下,Uncaught ReferenceError: VG is not defined 错误是什么原因导致的

  • chender (作者)

    找不到VG对象,确认一下界面初始化的时候js有没有报错,如果有报错可能导致上面贴的这个那个代码没正常初始化

    2015-09-28 10:33

  • 逃逸的风

    我估计他 子窗口 没包含上面的程序

    2015-09-28 10:49

  • jolynekujo

    回复 逃逸的风:是的,没有放在子页面,多谢了

    2015-09-28 10:53

撒网要见鱼

撒网要见鱼 - 厚积薄发!

感谢楼主提供的思路!
回头自己试试!

  • chender (作者)

    O(∩_∩)O

    2015-11-06 17:14

chender

chender (作者) - 与人为善

只要两个webview之间存在多个/多次不同的方法调用,就得用池
如果只有一个方法,并且不考虑线程安全(两个webview之间的js执行不是单线程的)的话,就用一个callback变量就行了

  • 闪闪

    父对子evalJS的时候,也并不知道对于子时机是否合适。

    2015-11-30 10:36

  • chender (作者)

    没有时机问题,如果有,也是业务上的,如果是业务上有时机问题,业务上的时机问题只有通过具体的代码逻辑去规避,从技术层面没有解决方案

    2015-11-30 10:40

立扬

立扬

如果是预加载的话用fire互相调用无所谓,但页面比较多不知道是没预加载完成还是页面多到预加载失败,现在只预加载了几个使用频率高的页面,给现载的页面传值并执行里面方法,在mui.plusReady(function() 第一次创建可执行,之后第二次调用现载的页面(已经创建好了我的理解就是跟预加载效果一样了) mui.plusReady(function()里面接收和执行方法 都不执行了。用fire 需要判断页面加载成功与否。如果设置每次打开现载页面都执行创建(设置“mui.openWindow的 createNew:false,//是否重复创建同样id的webview,默认为false:不重复创建,直接显示”)这样是不是不是很好?

  • chender (作者)

    这段js解决的是已经打开的两个webview之间的调用与回调问题

    2015-12-04 17:06

Lz_Ronny

Lz_Ronny

请问怎么在页面关闭前调用call方法?

  • chender (作者)

    重写mui.back

    2016-03-30 16:01

BruceCui

BruceCui

谢谢分享!!!

星陨

星陨 - 前端业余开发者

[ERROR] : TypeError: 'undefined' is not a function (evaluating 'callback(backParam && JSON.parse(backParam))')

  • chender (作者)

    你是不是在a页面中调用b页面,然后b页面回调的时候,a页面已经刷新过了,所以之前存储的回调函数就不存在了

    2016-12-14 10:54

  • 星陨

    感谢大神解答,我在仔细排查下

    2016-12-14 10:59

  • 星陨

    找到原因了,发现重复创建相同Id的页面了,plus.webview.getWebviewById(viewId)只返回第一次创建的

    2016-12-14 12:06

1***@qq.com

该问题目前已经被锁定, 无法添加新回复