[17年跨領域學習]從 WKWebview 再談混合開發 | 掘金技術徵文

做者:滴滴公共前端團隊 - 小春

前言javascript

首先祝福各位同窗新年快樂,17 年咱們在大前端領域討論點什麼呢?前端

這個問題我相信不少公司的前端負責人都會思考。這裏不做預言,只是帶着前端同窗們實實在在地一塊兒再來深刻地看看混合開發java

以前咱們會強調先後端交互的重要性,好比:ios

一、 POST 請求的 Content-Type 是 application/json 仍是 application/x-www-form-urlencoded,由於對應後端語言(PHP、Go、java 等) 獲取數據的方式會有差別。git

二、再好比不一樣後端處理跨域的方式。github

那相似的其實也有不少人也關注到前端和客戶端交互的重要性,畢竟如今的 APP 內嵌開發頻度要比傳統的 Web 網站多。
web

咱們以 WKWebview 這個切入點來談談所謂的混合開發。json

註釋後端

一、本文中會穿插大量的 iOS 代碼和詞彙,並且也會對 iOS 代碼風格和規範進行標註,來方便前端同窗瞭解 iOS 的部分細節實現。跨域

二、本文更可能是從一個前端 iOS 客戶端的雙重角度去看待混合開發。

若有錯誤請指正。

正文

很簡單的問題:咱們以往的前端代碼都在什麼環境(容器下)運行?

答案即是:瀏覽器(包含 PC 的各類瀏覽器以及手機上的瀏覽器)以及咱們先要介紹的 WebView

PC 瀏覽器內核的移植

對硬件原生 API 支持和 Webkit 特性都支持不夠

因此不少混合開發解決方案的第一個點就是:

作一個加強版本的 WebView

還有人記得 PhoneGap 嗎?

不少咱們早期的混合開發者基本都聽過或者用過它,尤爲被前端同窗喜歡:

由於開發都是用咱們擅長的前端技術開發 App

可是相比客戶端開發同窗會發現它有不少詬病:

由於它不是原生和前端混合使用

在 iOS 中內部爲 UIWebView

用於 iOS 網絡視圖加載網頁

它有哪些能力?

一、指定一個在線網頁地址,經過 NSURLRequest 類建立一個網絡請求

配合 UIWebViewloadRequst 來進行網絡視圖的加載

代碼實例

- (void) viewDidLoad {

[super viewDidLoad];

UIWebView *webview = [[UIWebView alloc] initWithFrame:self.view.frame];

NSURL *url = [NSURL URLWithString:@"https://zhuanlan.zhihu.com/ddfe-weekly"];

NSURLRequst *request = [NSURLRequest requestWithURL:url];

[webView loadRequest:request];

[self.view addSubview:webview];

}

給前端同窗的註釋:

// 語法內容

- :方法裏面的加號和減號,減號通常是對象方法

viewDidLoad :視圖的生命週期方法,和咱們前端的 onLoad 或者 jq 裏面的 ready 同樣,初始化用到

NSURL - 相似咱們的 Location 對象,能解析 URL

// 代碼風格規範

一、使用 4 個空格縮進,和前端開發規範同樣

二、方法的書寫:

* - 和 (void) 有一個空格,第一行結束的 { 在當前行的末尾

PS:聽說有一些公司的 C 語言規範是第一個大括號獨佔一行

二、UIWebView 類也支持加載 HTML 文件來實現遠程下載或者本地離線加載

經過 UIWebViewloadHTMLString

註釋:HTML 字符串引號須要轉義

三、代理(Delegate)

在哪定義當前視圖狀態呢?

就是:UIWebViewDelegate

通常咱們會定義 **ViewController 類

四、NSData 加載

通常針對圖片資源加載。

給前端同窗的註釋:

NSData 應用於文件讀取,能夠設置緩衝區

NSData 是不變緩衝區

NSMutableData 是可變緩衝區

五、咱們看看 UIWebView 源碼裏面都定義了哪些屬性和方法:

註釋:iOS 9.3 UIKit UIWebView.h

PS:

蘋果內部對 WebView 有緩存機制,部分打開過的資源第二次訪問的時候都會嘗試本地讀取,可是不太穩定,關掉以後,系統會清理它。

轉折點來了,這個事情也使得不少一部分同窗認識到一個新詞:

17年 1 月 6 號,微信團隊在公衆號發文:

微信 iOS 客戶端將於 3 月 1 日逐步升級爲 WKWebView 內核

WKWebView 又是什麼?

蘋果支持最新的 Webkit 功能

從 iOS 8 開始引入的網頁瀏覽控件(組件)

-- 高性能的 Web View 解決方案

好像是救世主?

一、運行消耗的內存明顯減少:App 啓動更快、穩定性更高

二、最新的 Web 標準

三、高達 60 fps 的滾動刷新率,內置手勢探測

等等

H5 和 APP 交互方式變了?

大部分的人都會提到 jsbridge 這個詞,那真正的內涵是什麼呢?

WebViewJavascriptBridge

一個解決 OC 和 Javascript 通信的 bridge 框架

An iOS/OSX bridge for sending messages between Obj-C and JavaScript in WKWebViews, UIWebViews & WebViews.

那原理究竟是什麼?

一、OC 經過 WebView 的 stringByEvaluatingJavaScriptFromString 來調用 js

二、js 調用 OC:

* iOS7 引入了 JavaScriptCore,能夠初始化一個 JSContext 對象,而後約定好一個方法名就行了。

* 通常也能夠經過私有協議 Scheme,客戶端會攔截指定的協議

* 還有人也提到輪詢,但我的感受這種方式在通常業務場景並非不少,除了個別特定場景,並且客戶端開銷也大

註釋:WebView 渲染是獨立線程,因此 js 代碼實際是異步的

說了這麼多,咱們看看源碼(pod 版本 6.0.2):

// WebViewJavascriptBridge/WebViewJavascriptBridge.m #103

- (NSString*) _evaluateJavascript:(NSString*)javascriptCommand {

return [_webView stringByEvaluatingJavaScriptFromString:javascriptCommand];

}

// WebViewJavascriptBridge/WebViewJavascriptBridge.m #178

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {

//...

}

固然細心的同窗會發現,它也支持 WKWebViews,咱們在源碼包裏面也看到了文件:

咱們也都知道在 H5 裏面能夠經過私有協議來喚起 App 以及細化到某個頁面。

對於 WKWebView 呢?

最先在 stackoverflow 上有一篇 Q:

《WKWebView and NSURLProtocol not working》

裏面也提到:

When using the old UIWebView you could catch the requests by implementing a custom NSURLProtocol. I use this to handle requests that requires authentication.

I tried the same code and it doesn't work with the new WKWebView but my protocol class isn't called at all.

咱們看看回答:

WKWebView makes requests and renders content out-of-process, meaning your app does not hear the requests they make.

If you are missing a functionality, now is the time to open a bug report and/or an enhancement request with Apple.

As of iOS 10.3 SDK, WKWebView is still unable to make use of custom NSURLProtocols using public APIs.

固然後面也貼了:

Enterprising developers have found an interesting method: +[WKBrowsingContextController registerSchemeForCustomProtocol:]

It supposedly adds the provided scheme to a list of custom protocol handled schemes and should then work with NSURLProtocol.

因此大部分的混合方案都是從入口 URL 攔截

這裏有幾個區別

WKWebView 攔截 decidePolicyForNavigationAction 方法

咱們能夠在上面提到的 WebViewJavascriptBridge 的 WKWebViewJavascriptBridge.m 源碼文件能夠看到:

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

//...

}

UIWebView攔截 shouldStartLoadWithRequest 代理方法(這個前面也提到了)

那不少人確定會問:切換到這個新的有什麼風險嗎?

一、看看是否頁面有適配問題

二、看看 jsbridge 是否有錯誤

三、看看是否有崩潰

我的以爲這些風險都是可控的,畢竟也會經歷一個灰度的過程。部分同窗也看過騰訊 Bugly 以前 1 月份發的《WKWebView 那些坑》的文章,裏面也提到了幾點和前端有關係的:

  • WKWebView 是多進程組件:Network Loading 和 UI Rendering 在其餘進程執行。在一些 WebGL 渲染的複雜頁面整體內存佔用也不低,過大的彷佛也會 crash 致使白屏

  • 頁面適配問題:好比調用 window.innerHeight 致使頁面被拉伸

  • 視頻自動播放的設置

  • goBack 回退上一頁不觸發 onload,也不會執行 js

  • Cookie 存儲的問題:WKWebView 發起的請求不會自動帶上存儲在 NSHTTPCookieStorage 容器的 Cookie;並且存儲時機會有延時。

這個就致使咱們以前的前端統一登陸組件換了一種方案去 hot fix,更多客源查看文末的原文連接。

咱們提一下豆瓣混合開發框架:Rexxar (以前也邀請來滴滴分享過一次)

它主要分 3 個部分:

  • Rexxar Web

  • Rexxar IOS

  • Rexxar Android

本文咱們重點看一下 iOS 的 Rexxar Container

容器 -- 其實就是一個內置的 WebView

可是增長了原生的一些功能支持:圖片緩存、Native UI 的調用等

說了這麼多,咱們看看源碼:

//rexxar-ios/Rexxar/Core/RXRViewController.h

/**

* 內置的 WebView。

*/

@property (nonatomic, strong, readonly) UIWebView *webView;

那不少專業的人要吐槽了:

Rexxar 採用了原生的 WebView,是對 App 體積沒影響

可是以前不少個 WebView 帶來的內存問題也一樣存在

-- 這個是滴

一樣的 Rexxar Container 和 Web 如何交互呢?

前面咱們介紹了 iOS 通常採用 WebViewJavascriptBridge,但它這裏不是,採用發送 HTTP 請求(套路基本你們都是採用 iframe 加載特殊約定的 URL),而後 Container 來攔截。其實相似 Proxy,Web 發出的請求都會被 Proxy 處理一下。

那這裏好像沒有用到 WKWebView,爲何呢?

這個咱們以前也在分享的時候請教過豆瓣的同窗,他們也嘗試過,畢竟咱們前面介紹了那麼多 WKWebView 的好處,大體的結論:

和他們的設計衝突

NSURLProtocol 沒法截獲 WKWebView 中的請求

固然以前也聽過美團大衆點評的 Hybrid 方案分享,他們的流程基本也相似:

一個 URL 請求在客戶端發起,有一個 Router 來查詢本地的路由配置表(這個配置表是 App 從後臺路由配置服務拉取的),根據對應的規則去跳轉到 H5 仍是 Native.

那如何評估一個混合應用的好壞呢,通常幾個維度:

一、開發效率高

其實你們發現後面的混合方案基本的初衷都是利用前端一些優秀的地方:模塊化,組件化,工程化。

固然客戶端和前端在協調開發的時候也有一些效率工具:好比客戶端加載一個前端的 demo 頁面,同時給前端打一個模擬器安裝包,以及相似 RN 這種 debugger 調試。

二、緩存帶來的加載快,資源文件能夠本地化,並且咱們能夠靈活配置化的管理緩存

  • 穩定

一、js 錯誤能夠經過 WebView 來捕獲,而後經過 App 日誌發送服務端來展現

二、WebView 的 Crash 也能夠採用 fabric 這些來收集

總結

17 年咱們會放出更多跨領域的內容,來提高前端同窗的事業,在互相協做的技術解決方案實施過程當中知其因此然。

加油 & 再次新年快樂。

同時感謝:滴滴 iOS 高工文傑老師對 iOS 代碼的指導

擴展閱讀:

github.com/marcuswesti…

www.infoq.com/cn/news/201…

mp.weixin.qq.com/s/rhYKLIbXO…

徵文活動:

掘金年度徵文 | 2016 與個人技術之路


歡迎關注DDFE
GITHUB:github.com/DDFE
微信公衆號:微信搜索公衆號「DDFE」或掃描下面的二維碼

相關文章
相關標籤/搜索