WebView与JS的交互手记

手机APP的开发越来越敏捷,很多地方如果可以用webView的形式代替原生开发,成为了不少团队的技术选择。webView虽然灵活,但是在操作体验和用户交互上还存在不少的缺陷。但因为webView应用的场景越来越多,原生WebView与JS的交互变得非常重要。本文主要记录了WebView与JS交互的几种主要方式,并且补充了自己使用过程中踩过的坑和心得。


方式1: WebViewJavascriptBridge

Issues


  • 解决方式一:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    if (webView != _webView) { return; }
    NSURL *url = navigationAction.request.URL;
    __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;
    if ([_base isWebViewJavascriptBridgeURL:url]) {
    if ([_base isBridgeLoadedURL:url]) {
    [_base injectJavascriptFile];
    } else if ([_base isQueueMessageURL:url]) {
    [self WKFlushMessageQueue];
    } else {
    [_base logUnkownMessage:url];
    }
    decisionHandler(WKNavigationActionPolicyCancel);
    } else {
    /// 在这新添加了else
    if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:decidePolicyForNavigationAction:decisionHandler:)]) {
    [_webViewDelegate webView:webView decidePolicyForNavigationAction:navigationAction decisionHandler:decisionHandler];
    } else {
    decisionHandler(WKNavigationActionPolicyAllow);
    }
    }
    }

  • 解决方式二:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    if (webView != _webView) { return; }
    NSURL *url = navigationAction.request.URL;
    __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;
    if ([_base isWebViewJavascriptBridgeURL:url]) {
    if ([_base isBridgeLoadedURL:url]) {
    [_base injectJavascriptFile];
    } else if ([_base isQueueMessageURL:url]) {
    [self WKFlushMessageQueue];
    } else {
    [_base logUnkownMessage:url];
    }
    decisionHandler(WKNavigationActionPolicyCancel);
    /// 在这新添加了return
    return;
    }
    if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:decidePolicyForNavigationAction:decisionHandler:)]) {
    [_webViewDelegate webView:webView decidePolicyForNavigationAction:navigationAction decisionHandler:decisionHandler];
    } else {
    decisionHandler(WKNavigationActionPolicyAllow);
    }
    }

方式2: JavascriptCore

前情提要

JavaScriptCore 是 Objective-C 的API,作用于链接JS语言和OC语言。通过较少的代码,开发者可以从OC运行JS或者从JS调用OC。

JavaScriptCore具有如下优势:

    1. 让你能够在UIWebView之外使用JS,运行简单的JS脚本无需依赖于web view.
    1. 可以使用现代OC的特性比如blocks和collection subscripting.
    1. 可以很方便的在两种语言之间进行值传递或者对象传递。
    1. 可以创建混合对象:OC的对象能够拥有JS的值甚至函数作为属性。(比如一个原生的按钮在点击的时候触发JS的函数)

合适的使用场景:

    1. 用在业务和原型经常会变化的地方。例如游戏。
    1. 用在不同的团队协作开发的时候。比如iOS程序员和前端程序员共同做一个项目。
    1. 用在需要能立刻看到变化的地方。JS语言是解释型的,所以更改JS代码我们可以看到app的实时变化。

原则上苹果不允许使用的场景:

    1. 从远程服务器下载并运行JS代码。(即使是开发者自己的服务器用内购的方式也不行)
    1. 用户在app内编写JS代码并运行。

JavaScriptCore 概览

主要的类和协议

    1. JSValue: 用来代表JS实体的OC对象。
    1. JSManagedValue: 本质上还是JSValue对象,但是引入了内存管理的特性用来协调OC的引用计数和JS的GC。
    1. JSContext:代表JS运行环境的对象。所有的JSValue对象都要依赖于JSContext。
    1. JSExport:协议。可以用这个协议把原生对象转化成JS对象,把原生的属性和方法转化成JS的属性和方法。
    1. JSVirtualMachine: 代表对象空间,拥有自己的heap和garbage collector.