從簡書iOS客戶端,來談談Hybrid方案細節設計

做爲一位 iOS 開發人員,你應該已經敏感地發現,本身的工做涉及內容已經不止於 Native 的部分,由於 Hybrid App 和 ReactNative 等技術方案已經不只僅是概念,愈來愈多的公司開始着手本身的 Hybrid 方案以及 ReactNative 本地化工做。前端

1、引言

介紹相關概念的優秀文章已經有許多,方案的實現原理你也應該已經或多或少有了一些理解。不瞭解也沒有關係,在這篇文章裏,我將用簡書 iOS 客戶端的有關特性,來探索一下 Hybrid 方案的技術細節。文章的目的是拋磚引玉,用一個具體的項目,你們很熟悉的簡書客戶端,來幫助你們認識 Hybrid 方案,而後親自實現它git

從如今開始,再也不着眼於某一個 feature ,你須要站在一個客戶端架構師的角度來看待問題。github

2、咱們用到的簡書客戶端特性

1.界面構成分析
  • 本文的主角是簡書 iOS 客戶端的文章展現頁面,這是個人一篇文章的展現頁面:
    文章展現頁面
    文章展現頁面
  • 如你所見,文章內容的展現是使用webview控件,具體是UIWebview仍是WKWebview按下不表,這不是本文的關鍵。在個人demo中,我使用了UIWebview
  • 在簡書發現tab欄的內容頂部,還有一個熱門內容推薦的輪播圖。與它相似是一些app內的活動推介輪播圖,以及廣告頁面,它們的詳情頁內容展現多使用webview。在簡書中,這個輪播圖對應的下一級頁面也是文章展現頁面,特性基本一致。
    熱門內容推薦輪播圖
    熱門內容推薦輪播圖
  • 在 webview 的基礎上,添加了符合瀏覽器用戶習慣的導航欄按鈕。包括左側的返回關閉按鈕。以及右側的功能列表按鈕。
  • 頁面底部,是一個工具欄,提供了四個經常使用的操做。注意這裏的評論按鈕,它是咱們下文的一個談論點。
2.界面特性分析
  • 通常各家客戶端的內容頁,都會有一些適於本身功能點的設計。簡書也不例外。好比,在文章內容區域點擊做者的頭像(它自己也是網頁的一部分,暫且理解爲對應一個連接),跳轉到了做者的我的主頁,注意,容易發現它是一個客戶端的原生頁面,也就是一個VC。
    做者我的主頁
    做者我的主頁
  • 點擊底部工具欄中的評論按鈕(原生組件),頁面(web頁面)會滑動到評論區域,如圖
    點擊評論按鈕,頁面滑動
    點擊評論按鈕,頁面滑動
  • 對一篇文章寫下本身的評論(使用了原生組件),評論列表(網頁內容)進行更新。
  • 簡書對於展現內容做了內外站的區分。據我本身的簡要測試,來自簡書域名www.jianshu.com下的內容,在加載過程當中,是沒有進度條的,用戶體驗很是接近原生頁面。而第三方的內容,則在加載過程當中會出現通常瀏覽器中常見的加載進度條,如圖:
    第三方內容的加載進度條
    第三方內容的加載進度條
  • 對於簡書域名下的內容,不會出現叉號的關閉按鈕,這也是爲了營造接近原生頁面的用戶體驗,讓用戶不會察覺到這是一個 web 界面。而第三方內容,則會出現符合瀏覽器使用習慣的關閉按鈕,如上圖。

3、咱們須要的儲備知識

1.Hybrid相關
  • 在Hybrid架構中,原生界面和web頁面須要頻繁地溝通,而且是雙向的溝通。原生代碼能夠構建JavaScript語句,交由webview進行執行,從而在web頁面上實現須要的效果。而在web頁面的js文件中,也能夠調用原生的Objective-C方法,從而執行一些原生方法才能完成的操做。與此相關的庫有WebViewJavascriptBridge以及JavaScriptCore,有須要的同窗能夠自行了解。
2.UIWebview的相關特性
  • UIWebviewDelegate
    webview的代理方法你們想必很是熟悉,咱們能夠在頁面加載前、開始加載時、加載完成時以及失敗時進行須要的操做。這裏咱們須要用到的是這一條代理方法:
    - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
    webview根據它的返回結果來決定是否進行加載。
  • 執行JS語句的方法:
    - (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
    咱們能夠自行構建一條JS語句,經過這個方法交由webview執行
  • goback相關
    UIWebview擁有布爾類型的canGoBackloading等屬性,經過監測它們的值咱們能夠知道當前頁面是否能夠進行回退,以及頁面是否正在加載。
    與之對應,擁有- (void)goBack;等方法,調用之頁面會進行返回,就像咱們在瀏覽器中常見的那樣。

4、相關特性的模仿實現

對於上文中提到的相關特性,我出寫了一個demo,對它們進行了簡要的模仿實現。固然簡書官方的實現會考慮到方方面面,而個人demo僅是從Hybrid架構的思想出發,盼可以拋磚引玉。
這是demo中對該頁面的模仿實現:
web

demo頁面
demo頁面

1.頁面初始化

在demo中,使用一條web頁面的URL來初始化VC:- (instancetype)initWithURL:(NSURL *)URL;這條URL對應文章的連接。
頂部導航欄和底部工具欄都是系統原生的UINavigationBarUIToolBar,按鈕素材使用阿里巴巴的iconfont字體。json

2.點擊做者頭像進入我的主頁

關於這個特性的實現,若是按照 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做爲代理,這時再使用源代碼查看,看到的就是移動端的網頁源代碼了。服務器

3.點擊評論按鈕,頁面滑動到評論區域

這個特性的實現方式和上面相似,點擊評論按鈕,原生代碼構建一條JS語句,交由- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;方法進行執行,由web頁面執行滑動操做。代碼以下:架構

- (void)scrollToCommentField
{
    [self stringByEvaluatingJavaScriptFromString:@"scrollTo(0,20500)"];
}複製代碼

這裏的JS語句很是簡單,因爲筆者的前端知識還有所欠缺,沒有想到能夠精確滑動的評論區域的JS語句,因此簡要實現,點到爲止。app

4.原生組件寫評論,web頁面更新

這裏首先須要貼一下文章頁面的網頁源代碼:

<!-- 評論列表 -->
  <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代碼,從而看到本身發的新評論。

5.內外站頁面的區分

這裏和點擊頭像的實現方法相似,經過攔截連接的URL,區份內部連接和第三方連接,從而在開始加載的時候採用不一樣的加載界面,或者對於第三方連接單獨開啓一個第三方VC。
demo中關於第三方連接的關閉按鈕的顯示邏輯,作出了相應的處理。

5、demo中的不足

看到這裏你們應該就會發現,對於提到Hybrid咱們就會想到的Bridge、Router等模塊,我並無作明顯的限定。這樣也是爲了方便你們用一種更接近以往原生代碼編寫的思惟,來理解Hybrid模式。
同時,demo中較多涉及了原生代碼對web頁面作出的溝通操做。而沒有JS代碼對原生代碼的調用,這是由於一來站在一個簡書客戶端的用戶和iOS開發的角度,對於JS端執行的操做,有些力不能及,這本是和你共同工做的前端夥伴的任務,二來對於一篇幫助你們入門Hybrid的文章來講,從這個單方面的交互來入手,管中窺豹,已經是足夠。

6、一些感悟

其實,寫了這麼多,我以爲收穫到一些感悟是最重要的,下面的要講的,多是我以爲更爲重要的思想性的東西。

1.將來的趨勢之一,即是大前端團隊進行客戶端開發。
  • 看到這裏你發現,若是大家的團隊想要採用Hybrid模式進行產品的開發,光靠iOS或者是安卓的客戶端工程師是不可能完成的。在客戶端框架的開發過程當中,須要和前端的工程師溝通具體的技術細節。好比怎樣設計接口可以更好地兼顧客戶端和前端特色,對於某個問題,如何能把握全局而不是單單從客戶端的角度來看待。這些多是普通的iOS開發工程師和大牛的差距所在之一。
  • 愈來愈多的客戶端工程師招聘要求中,出現了熟悉前端語言的要求。若是你能在精通客戶端開發之餘,對前端語言也遊刃有餘,那麼在接下來的發展趨勢中,就會有更多的可能性。因此,請開始你的前端學習吧~。
2.在Hybrid模式下,如何進行產品技術方案的取捨
  • 如前文所見,簡書客戶端對於內部域名的內容和第三方內容,在展現方式上是有明顯不一樣的。在閱讀簡書的文章時,讓用戶發現不了本身是在一個瀏覽器上進行閱讀,這在方方面面就極大改善了用戶體驗。爲了作到這一點,我推測簡書首先須要對本身的內容進行很是良好的CDN加速,以保證內容加載時不會耗時過長,同時採起一些預加載策略,二是在內容加載時,採用與原生界面部分相同的loading界面,去掉進度條,模擬原生界面的加載過程。而對於第三方的連接,採用進度條+返回、關閉按鈕的設計,則更符合用戶在瀏覽器中進行閱讀的習慣,也能夠和本身的內容進行直觀區分,這也改善了用戶體驗。
  • 對於某些原生和Web頁面均可以實現的特性如何取捨,這也是須要考慮的問題。好比,點擊評論按鈕頁面滑動,這個功能使用web頁面的滑動而非原生的控制顯然更爲天然也更符合用戶習慣。而對於撰寫評論的功能,使用原生的鍵盤、編輯器組件,固然就比使用web頁面的鍵入更加穩定可控了。

7、文章的demo

對於文章內容,我寫了一個demo,這是demo地址爲了便於理解,我爲代碼寫了詳盡的註釋。若是以爲它對你有幫助,不妨在github上爲我點一個star~很是感謝!

相關文章
相關標籤/搜索