效果

这里写图片描述
这里写图片描述

项目地址

UIWebView与JS的深度交互

事情的起因还是因为项目需求驱动。折腾了两天,由于之前没有UIWebView与JS交互的经历,并且觉得这次在功能上有一定的创造性,特此留下一点文字,方便日后回顾。
参考 Github上看到了WebViewJavascriptBridge 这个用于UIWebView/WebViews和JS交互的封装库。

  1. 一开始,我们在Native端和JS端都分别进行初始化:
    • OC端:
@property WebViewJavascriptBridge* bridge; 

对应的初始化代码如下,在初始化中直接包含了一个用于接收JS的回调:

_bridge = [WebViewJavascriptBridge bridgeForWebView:webView webViewDelegate:self handler:^(id data, WVJBResponseCallback responseCallback) {  
        NSLog(@"ObjC received message from JS: %@", data);
        responseCallback(@"Response for message from ObjC");
}];
  • JS端:(以下是固定写法,你自己的JS文件中必须包含如下代码)
function connectWebViewJavascriptBridge(callback) {  
    if (window.WebViewJavascriptBridge) {
        callback(WebViewJavascriptBridge)
    } else {
        document.addEventListener('WebViewJavascriptBridgeReady',   function() {
            callback(WebViewJavascriptBridge)
        }, false)
    }
}
connectWebViewJavascriptBridge(function(bridge) {  
    bridge.init(function(message, responseCallback) {
        log('JS got a message', message)
        var data = { 'Javascript Responds':'Wee!' }
        log('JS responding with', data)
        responseCallback(data)
    })
}
  1. 同理,第二条,在JS中调用了bridge.callHandler(‘testJavascriptHandler’),它将触发OC端注册的同名方法:
    原理图

以上表中的对应关系的解读是,例如第一条:在JS中如果调用了bridge.send(),那么将触发OC端_bridge初始化方法中的回调。
同理,第二条,在JS中调用了bridge.callHandler(‘testJavascriptHandler’),它将触发OC端注册的同名方法:

bridge.registerHandler('testJavascriptHandler', function(data, responseCallback) {  
        log('ObjC called testJavascriptHandler with', data)
        var responseData = { 'Javascript Says':'Right back atcha!' }
        log('JS responding with', responseData)
        responseCallback(responseData)
})

了解了使用规则,下面来看看在我们这个实际需求中应用的整体思路:
y

基于以上的思路,使用vue实现 和 webview的交互

  1. 原生调用vue中的方法

    由于vue中作用域的关系,所以交互的事件必须挂载到window上,那么实现思路就是写一个vue的plugin(插件),并且把这个插件对象挂载到window上,代码如下:

        var hybrid = {
             install (Vue, options) {
                // 把当前的对象挂载到vue的全局
                Vue.prototype.$hybrid = this;
            }
        }
        // 把vue中绑定的对象挂载到window上
        window.Hybrid = hybrid;
        // 导出共给vue组件中引入使用
        export default hybrid
    

    android 中执行调用:

     webView.loadUrl("javascript:Hybrid.registerHandler('android', 'jsActivityShare')");

    ios 中执行:

    // 方法一.   JSValue *function = [self.context objectForKeyedSubscript:@"factorial"];
    // 方法二.
    JSValue * function = self.context[@"factorial"];
    JSValue *result = [function callWithArguments:@[inputNumber]];
    self.showLable.text = [NSString stringWithFormat:@"%@", [result toNumber]];
  2. vue中的方法调原生

    需求:实现一个处理原生的数据 然后回掉原生方法, 代码如下:

        // 注册原生发过来的方法,name需要注册的方法的名字
    registerHandler (name, callBack) {
        console.log(name + "js 处理原生发送的数据");
        try {
            // eval(this.native.callBack) 必须指定清楚那个对象的函数名字
            if (typeof(eval(this.native.callBack)) == "function") {    
                eval("this.native." + callBack + "('js处理完成之后的结果回传给原生客户端');");
            } else {
                // 函数不存在, 抛出异常提示函数不存在
                alert(callBack);
            }
        }catch(err){
            alert(err) // 可执行
        }
    }
    
  3. 原生调用vue组件中的函数

    需要使用vue的广播事件,在vue的全局挂载一个事件对象,然后由这个事件对象统一处理事件,代码如下:

    Vue.prototype.$eventHub= Vue.prototype.$eventHub || new Vue({data:{'message':''}, template:'<div> {{message}} </ div>'});
        this.$eventHub = Vue.prototype.$eventHub;
    

    然后发送广播事件

     //调用$emit 方法, 来发送全局的函数
        this.$eventHub.$emit('event', yourData);

    最后在组件中接受广播事件

        // 监听app中发出的事件
    this.$eventHub.$on('event', (index)=>{
      TODO:: // 调用组件内容函数处理
    });

参考:
1. IOS中 使用JavaScriptCore 实现OC与JS的交互
2. UIWebView与JS的深度交互
3. 10分钟教你快速开发一个vue插件并发布npm

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐