做爲一位 iOS 開發人員,你應該已經敏感地發現,本身的工做涉及內容已經不止於 Native 的部分,由於 Hybrid App 和 ReactNative 等技術方案已經不只僅是概念,愈來愈多的公司開始着手本身的 Hybrid 方案以及 ReactNative 本地化工做。前端
介紹相關概念的優秀文章已經有許多,方案的實現原理你也應該已經或多或少有了一些理解。不瞭解也沒有關係,在這篇文章裏,我將用簡書 iOS 客戶端的有關特性,來探索一下 Hybrid 方案的技術細節。文章的目的是拋磚引玉,用一個具體的項目,你們很熟悉的簡書客戶端,來幫助你們認識 Hybrid 方案,而後親自實現它 。git
從如今開始,再也不着眼於某一個 feature ,你須要站在一個客戶端架構師的角度來看待問題。github
webview
控件,具體是UIWebview
仍是WKWebview
按下不表,這不是本文的關鍵。在個人demo中,我使用了UIWebview
。發現
tab欄的內容頂部,還有一個熱門內容推薦的輪播圖。與它相似是一些app內的活動推介輪播圖,以及廣告頁面,它們的詳情頁內容展現多使用webview。在簡書中,這個輪播圖對應的下一級頁面也是文章展現頁面,特性基本一致。返回
和關閉
按鈕。以及右側的功能列表按鈕。評論
按鈕(原生組件),頁面(web頁面)會滑動到評論區域,如圖www.jianshu.com
下的內容,在加載過程當中,是沒有進度條的,用戶體驗很是接近原生頁面。而第三方的內容,則在加載過程當中會出現通常瀏覽器中常見的加載進度條,如圖:關閉
按鈕,這也是爲了營造接近原生頁面的用戶體驗,讓用戶不會察覺到這是一個 web 界面。而第三方內容,則會出現符合瀏覽器使用習慣的關閉
按鈕,如上圖。JavaScript
語句,交由webview進行執行,從而在web頁面上實現須要的效果。而在web頁面的js文件中,也能夠調用原生的Objective-C
方法,從而執行一些原生方法才能完成的操做。與此相關的庫有WebViewJavascriptBridge以及JavaScriptCore
,有須要的同窗能夠自行了解。- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
UIWebview
擁有布爾類型的canGoBack
、loading
等屬性,經過監測它們的值咱們能夠知道當前頁面是否能夠進行回退,以及頁面是否正在加載。- (void)goBack;
等方法,調用之頁面會進行返回,就像咱們在瀏覽器中常見的那樣。對於上文中提到的相關特性,我出寫了一個demo,對它們進行了簡要的模仿實現。固然簡書官方的實現會考慮到方方面面,而個人demo僅是從Hybrid架構的思想出發,盼可以拋磚引玉。
這是demo中對該頁面的模仿實現:
web
在demo中,使用一條web頁面的URL來初始化VC:- (instancetype)initWithURL:(NSURL *)URL;
這條URL對應文章的連接。
頂部導航欄和底部工具欄都是系統原生的UINavigationBar
和UIToolBar
,按鈕素材使用阿里巴巴的iconfont字體。json
關於這個特性的實現,若是按照 Hybrid 架構的思想,屬於 Web 頁面調用原生方法,進入一個原生的VC。點擊頭像,JS腳本執行相關代碼,調用原生方法暴露出來的接口,執行原生方法。
我在這裏用一種簡要的方法實現:原生代碼利用以前提到的代理方法,在用戶點擊頭像後,攔截該URL,分析URL爲頭像部分,直接執行原生方法跳轉到我的主頁VC。
經過分析簡書文章頁面的網頁源代碼,我發現用戶頭像對應的URL
中的Query
部分,有一個參數爲utm_medium=note-author-link
。據此,在- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
代理方法中加以判斷,如果頭像連接,則跳轉到我的主頁VC。下面是相關代碼:瀏覽器
NSURL *destinationURL = request.URL;
NSString *URLQuery = destinationURL.query;
// 簡書點擊文章中頭像時跳轉至原生頁面。此處利用頭像連接中的一個參數做判斷
if ([URLQuery containsString:@"utm_medium=note-author-link"])
{
NSLog(@"我跳轉到我的主頁啦");
AvatorViewController *avatorVC = [[AvatorViewController alloc] init];
[self.navigationController pushViewController:avatorVC animated:YES];
return NO;
}複製代碼
最後返回NO是由於如果頭像連接,該web頁面是不須要作跳轉操做的。bash
這裏順便講一個小tips:若是想要在Mac端查看移動端的網頁源代碼,那麼你只須要在Safari中輸入該頁面,而且在
開發
選項下的用戶代理
中,選擇iOS系統下的Safari做爲代理,這時再使用源代碼查看,看到的就是移動端的網頁源代碼了。服務器
這個特性的實現方式和上面相似,點擊評論按鈕,原生代碼構建一條JS語句,交由- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
方法進行執行,由web頁面執行滑動操做。代碼以下:架構
- (void)scrollToCommentField
{
[self stringByEvaluatingJavaScriptFromString:@"scrollTo(0,20500)"];
}複製代碼
這裏的JS語句很是簡單,因爲筆者的前端知識還有所欠缺,沒有想到能夠精確滑動的評論區域的JS語句,因此簡要實現,點到爲止。app
這裏首先須要貼一下文章頁面的網頁源代碼:
<!-- 評論列表 -->
<div data-vcomp="comments-list" data-lazy="3">
<script type="application/json">
{"likedNote":true,"commentable":true,"publicCommentsCount":3,"noteId":2491941,"likesCount":43}
</script>
</div>複製代碼
能夠看到,頁面的評論內容是異步加載的。因此這個功能的實現,我
認爲比較合理的邏輯是原生組件向服務器提交一條新的評論,收到成功回調以後,原生組件和web頁面進行交互,執行更新並加載評論列表的JS代碼,從而看到本身發的新評論。
這裏和點擊頭像的實現方法相似,經過攔截連接的URL,區份內部連接和第三方連接,從而在開始加載的時候採用不一樣的加載界面,或者對於第三方連接單獨開啓一個第三方VC。
demo中關於第三方連接的關閉按鈕的顯示邏輯,作出了相應的處理。
看到這裏你們應該就會發現,對於提到Hybrid咱們就會想到的Bridge、Router等模塊,我並無作明顯的限定。這樣也是爲了方便你們用一種更接近以往原生代碼編寫的思惟,來理解Hybrid模式。
同時,demo中較多涉及了原生代碼對web頁面作出的溝通操做。而沒有JS代碼對原生代碼的調用,這是由於一來站在一個簡書客戶端的用戶和iOS開發的角度,對於JS端執行的操做,有些力不能及,這本是和你共同工做的前端夥伴的任務,二來對於一篇幫助你們入門Hybrid的文章來講,從這個單方面的交互來入手,管中窺豹,已經是足夠。
其實,寫了這麼多,我以爲收穫到一些感悟是最重要的,下面的要講的,多是我以爲更爲重要的思想性的東西。
進度條+返回、關閉按鈕
的設計,則更符合用戶在瀏覽器中進行閱讀的習慣,也能夠和本身的內容進行直觀區分,這也改善了用戶體驗。對於文章內容,我寫了一個demo,這是demo地址。爲了便於理解,我爲代碼寫了詳盡的註釋。若是以爲它對你有幫助,不妨在github上爲我點一個star~很是感謝!