时至今日,我都在想“微信小程序”为什么不能做成Web式,而是要去加那么一层隔离,终归其原因,还是随着时间向前走,Hybrid的载体也发生了变化,不然该卡的还是一样卡的一逼。从iOS的角度上来说载体从UIWebView变成了WKWebView,Android有着他们自研的X5当然原生的内核,如果你用着Android7.0也不见得会卡,这才是小程序能出来的根本原因,没有载体,一切都是空谈。
今天,我们谈一谈iOS的载体“WKWebView”,有兴趣的朋友可以直接阅读:https://developer.apple.com/reference/webkit/wkwebview ,当然你也可以接着往下看,我对于他的理解,苹果在iOS8中推出的新框架“Webkit”,其中WKWebView就是用来替换原来的UIWebView,一句话,你用它原来UIWebView出现的各种问题都被解决了。当然随之而来的会有一些小问题,比如:WKWebView是一个独立进程,那么它的请求就无法通过系统的URL SYSTEM了,你无法像UIWebView一样,可以通过NSURLProtocol来拦截所有的请求。
如下都简称WK
正常情况下,我们做Hybrid容器基本会用到WKWebView几乎全部的特性,但是也有三个其中重中之重的地方,那就是JavaScript的交互与网页应用的性能监控。至于你想到的如何加载网页,其实很简单,一个load而已。
self.wkWebView?.loadHTMLString(html, baseURL: URL(string: "https://github.com/icepy"))
说到JavaScript交互比较重要的地方是需要实现“WKScriptMessageHandler”协议的“userContentController”方法,这是从JavaScript向Native发送消息的主要渠道,当然如果你用协议的方式也不是不行,至少这个协议的实现它帮你完成了JavaScript到Native类型的转换,比如JavaScript的对象可以转为Dictionary对应的其他类型也是如此。
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
// js 调 App方法传消息过来
let body:Dictionary = message.body as! Dictionary<String,String> print(body)
let vdom = ["key":"name"];
do {
let data:Data = try JSONSerialization.data(withJSONObject: vdom, options: JSONSerialization.WritingOptions.prettyPrinted)
let jsonString:String = String(data: data, encoding: .utf8)!
print("\(jsonString)")
self.wkWebView?.evaluateJavaScript("receiveNativeEvent(\(jsonString))", completionHandler: { (data, error) in
});
} catch {
print("JSON Serialization Error")
}
}
那么JavaScript该如何调用发消息过来呢?在初始化WKWebView时你还需要配置一个Conf,这个Conf中你可以添加一个属性,这个属性在JavaScript这一边你可以通过window.webkit.messageHandlers.icepyApp获得。
let config = WKWebViewConfiguration();
config.userContentController.add(self, name: "icepyApp");
self.wkWebView = WKWebView(frame: self.view.frame, configuration: config)
最终当你需要向Native发送消息时就需要使用这个属性并调用其postMessage方法,比如:
function sendMessage (){
window.webkit.messageHandlers.WinexApp.postMessage.apply(window,arguments);
}
Native向JavaScript发送消息就更不用说了,直接调用“evaluateJavaScript”方法注入就好,唯一的优势是在于,发送的消息可以先转成JSON,然后字符串化当参数传入到一个函数里,而你的函数真实接收到的是一个对象,而不是字符串,这就是WKWebView辅助我们做了很多这样的类型转换的事情,如果是UIWebView就没有这么方便的办法了。
说完JavaScript与Native的交互,我还想谈一个非常重要的事情:关于监控,这是一个Hybrid应用的重中之重,只有良好全面的监控,你才能知道应用的运行状态,才能及时的做出判断,来优化应用,更好的服务用户。我们知道WKWebView是一个独立的进程,它的请求都不经过系统的URL SYSTEM,我们很难拦截它,该怎么办?在此之前,我们应该先监控好一个应用打开的完整状态,你需要实现WKNavigationDelegate协议,如下几个方法:
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
// 页面开始加载时调用
}
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
// 当内容开始返回时调用
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
// 页面加载完成
}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
// 页面加载错误
print(error)
}
每一个delegate实现中你都应该去做一条日志的记录或者是页面加载完成时间,说到页面加载完成时间肯定是从didStart开始经过didCommit最后didFinish的累加,这个时间不是渲染时间,渲染时间在客户端上是很难统计的,我的建议是做一个JS-API,让Web应用主动的提供渲染完成时间,客户端这边从页面加载完成开始计时,选择一个你认为比较合理的渲染时间,当Web报时大于它时,肯定渲染就不符合预期,这个时候,你还需要从另外的角度去分析问题了,我的建议是使用performance再加上DOM Ready ,全局Error,来定位具体的问题。
对于页面的跳转是否运行,你也可以进行监控,当然在这里只需要实现另外三个协议,比如:
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
}
在请求发起之前来决定是否跳转,而这里也是可以写上一条日志的。
关于监控是一个非常复杂的学科,要融合方方面面根据你业务特点的卡位,日久积累下来的经验,肯定有用武之地的。
你身边如果有朋友对混合领域(跨技术栈)或全栈,编程感悟感兴趣,可以转发给他们看哦,^_^先谢过啦。