跟Native开发相比, 网页开发虽然在用户体验上跟Native有不小的差距, 但其在开发周期、更新方式上却有不可比拟的优势。有时为了快速上线新的业务或动态支持第三方的业务,App中不可避免的会使用webview。一般情况下, webview或多或少的都会用到cookie、定位、支付、分享等信息或功能,而这些功能却是Native; 另一方面,统一UI交互:选择列表、信息提示等对提升用户体验也非常有帮助,直接使用Native显然又要比使用web来模拟Native的UI要简单的多。综合来看,webview调用Native模块完成部分交互是必不可少的,那么,如何做到呢?
Native vs Web
App开发的同学们都知道从产品体验的角度出发,webview绝不是App的第一选择,跟Native相比webview在产品体验上有下面几个方面的不足:
- 页面渲染慢。网页的资源加载、UI位置计算、渲染、逻辑运算等都是单线程,无法比拟Native多线程
- 流畅性不够好。eg. 上下滑动页面时, Native的滚动流畅性要远高于WebView,可以拿两个列表页试试
- 手势的支持比较少。webview支持的手势要比Native少,比如多点触控、ShakeGesture等
- 很难做到UI的一致。用户首先对系统原生的UI控件比较熟悉,App的开发大都基于这些基本的控件。使用webview时,由于不能直接使用很多Native原生控件,而用JS实现的控件跟原生又有差异,所以UI的一致性是大有折扣的。即使Web控件的样式跟原生差别很小,iOS 每年的系统更新带来的UI跳转就会破坏这些花大力气实现的UI
但从业务的角度出发,有时我们不得不选择使用webview,这主要基于以下几个方面的考虑:
- webview开发周期短,可以随时上线更新
- 出于发版的考量,一些业务功能Native开发的周期过长,需要webview接入快速上线
- 有些服务来自第三方,App开发者本身不需要支持这些服务, 使用webview展示第三方服务
OC中的webview
UIWebView始于iOS2.0(WKWebView iOS8.0), 网页跟Native交互的API非常简单:
- 通过delegate, 可以监听网页的load事件(startload, finishload, error)
- 调用 stringByEvaluatingJavaScriptFromString:,可以让网页执行一段JS代码, 返回值为字符串
OC与JavaScript
iOS7.0之前,stringByEvaluatingJavaScriptFromString: 是OC执行JavaScript代码的唯一方式; iOS7.0的发布带来了JavaScriptCore Framework,这是对WebKit’s JavaScript Engine的一个封装,无需借助UIWebView 就可以直接以Native的方式执行JavaScript代码。
1. JavaScript in String
1 | JSContext *context = [[JSContext alloc] init];//新建一个JS的执行环境, 可建多个互不影响的Context |
2. JavaScript in Native
1 | //以Native的方式定义一个函数,可以在JS中直接调用, 其实就是把一个Native函数导出到JSContext |
两种JavaScriptBridge的实现
1. 开源项目 WebViewJavaScriptBridge
这是个著名的开源项目,几乎不限制iOS的系统版本,实现方式比较简单但逻辑却比较绕, 核心的思想就是JavaScript通过load事件给Native发送消息, 之后Native通过stringByEvaluatingJavaScriptFromString: 完成通信,下图可以描述核心思想:
MTNB的实现基于JavaScriptCore, 相比于上一个开源项目, MTNB的实现更加简单、优雅、自定义功能扩展更加方便,另外,MTNB基于安全考虑,网页端加入了鉴权部分, 使用可以参考 官方原理 、接入备忘。MTNB需要Native跟JS的配合, 下边是MTNB的的实现描述:
[JavaScript Code]
1 | //定义一些变量... |
[Object-C code]
1 | //MTNBExport.h |
后记
WKWebView作为UIWebView的替代者, 随着iOS8.0的发布浮出水面。跟UIWebView相比, WKWebView渲染网页的速度更快,提供的接口也更加丰富,但这并不意味着我们可以放弃Bridge,相反,当App支持的iOS版本提升到8.0的时候,要考虑同WKWebView替换UIWebView升级Bridge