作為一位 iOS 開發(fā)人員允蜈,你應(yīng)該已經(jīng)敏感地發(fā)現(xiàn)冤吨,自己的工作涉及內(nèi)容已經(jīng)不止于 Native 的部分,因為 Hybrid App 和 ReactNative 等技術(shù)方案已經(jīng)不僅僅是概念饶套,越來越多的公司開始著手自己的 Hybrid 方案以及 ReactNative 本地化工作漩蟆。
一、引言
介紹相關(guān)概念的優(yōu)秀文章已經(jīng)有許多妓蛮,方案的實現(xiàn)原理你也應(yīng)該已經(jīng)或多或少有了一些理解怠李。不了解也沒有關(guān)系,在這篇文章里,我將用簡書 iOS 客戶端的有關(guān)特性捺癞,來探索一下 Hybrid 方案的技術(shù)細節(jié)夷蚊。文章的目的是拋磚引玉,用一個具體的項目髓介,大家很熟悉的簡書客戶端惕鼓,來幫助大家認(rèn)識 Hybrid 方案,然后親自實現(xiàn)它 唐础。
從現(xiàn)在開始箱歧,不再著眼于某一個 feature ,你需要站在一個客戶端架構(gòu)師的角度來看待問題一膨。
二呀邢、我們用到的簡書客戶端特性
1.界面構(gòu)成分析
-
本文的主角是簡書 iOS 客戶端的文章展示頁面,這是我的一篇文章的展示頁面:
- 如你所見豹绪,文章內(nèi)容的展示是使用
webview
控件驼鹅,具體是UIWebview
還是WKWebview
按下不表,這不是本文的關(guān)鍵森篷。在我的demo中输钩,我使用了UIWebview
。 - 在簡書
發(fā)現(xiàn)
tab欄的內(nèi)容頂部仲智,還有一個熱門內(nèi)容推薦的輪播圖买乃。與它類似是一些app內(nèi)的活動推介輪播圖,以及廣告頁面钓辆,它們的詳情頁內(nèi)容展示多使用webview剪验。在簡書中,這個輪播圖對應(yīng)的下一級頁面也是文章展示頁面前联,特性基本一致功戚。
- 在 webview 的基礎(chǔ)上,添加了符合瀏覽器用戶習(xí)慣的導(dǎo)航欄按鈕似嗤。包括左側(cè)的
返回
和關(guān)閉
按鈕啸臀。以及右側(cè)的功能列表按鈕。 - 頁面底部烁落,是一個工具欄乘粒,提供了四個常用的操作。注意這里的評論按鈕伤塌,它是我們下文的一個談?wù)擖c灯萍。
2.界面特性分析
-
一般各家客戶端的內(nèi)容頁,都會有一些適于自己功能點的設(shè)計每聪。簡書也不例外旦棉。比如齿风,在文章內(nèi)容區(qū)域點擊作者的頭像(它本身也是網(wǎng)頁的一部分,暫且理解為對應(yīng)一個鏈接)绑洛,跳轉(zhuǎn)到了作者的個人主頁聂宾,注意,容易發(fā)現(xiàn)它是一個客戶端的原生頁面诊笤,也就是一個VC系谐。
- 點擊底部工具欄中的
評論
按鈕(原生組件),頁面(web頁面)會滑動到評論區(qū)域讨跟,如圖
- 對一篇文章寫下自己的評論(使用了原生組件),評論列表(網(wǎng)頁內(nèi)容)進行更新晾匠。
- 簡書對于展示內(nèi)容作了內(nèi)外站的區(qū)分茶袒。據(jù)我自己的簡要測試,來自簡書域名
www.reibang.com
下的內(nèi)容凉馆,在加載過程中薪寓,是沒有進度條的,用戶體驗非常接近原生頁面澜共。而第三方的內(nèi)容向叉,則在加載過程中會出現(xiàn)一般瀏覽器中常見的加載進度條,如圖:
- 對于簡書域名下的內(nèi)容嗦董,不會出現(xiàn)叉號的
關(guān)閉
按鈕母谎,這也是為了營造接近原生頁面的用戶體驗,讓用戶不會察覺到這是一個 web 界面京革。而第三方內(nèi)容奇唤,則會出現(xiàn)符合瀏覽器使用習(xí)慣的關(guān)閉
按鈕,如上圖匹摇。
三咬扇、我們需要的儲備知識
1.Hybrid相關(guān)
- 在Hybrid架構(gòu)中,原生界面和web頁面需要頻繁地溝通廊勃,并且是雙向的溝通懈贺。原生代碼可以構(gòu)建
JavaScript
語句,交由webview進行執(zhí)行供搀,從而在web頁面上實現(xiàn)需要的效果隅居。而在web頁面的js文件中钠至,也可以調(diào)用原生的Objective-C
方法葛虐,從而執(zhí)行一些原生方法才能完成的操作。與此相關(guān)的庫有WebViewJavascriptBridge以及JavaScriptCore
棉钧,有需要的同學(xué)可以自行了解屿脐。
2.UIWebview的相關(guān)特性
- UIWebviewDelegate
webview的代理方法大家想必非常熟悉,我們可以在頁面加載前、開始加載時的诵、加載完成時以及失敗時進行需要的操作万栅。這里我們需要用到的是這一條代理方法:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
webview根據(jù)它的返回結(jié)果來決定是否進行加載。 - 執(zhí)行JS語句的方法:
- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
我們可以自行構(gòu)建一條JS語句西疤,通過這個方法交由webview執(zhí)行 - goback相關(guān)
UIWebview
擁有布爾類型的canGoBack
烦粒、loading
等屬性,通過監(jiān)測它們的值我們可以知道當(dāng)前頁面是否可以進行回退代赁,以及頁面是否正在加載扰她。
與之對應(yīng),擁有- (void)goBack;
等方法芭碍,調(diào)用之頁面會進行返回徒役,就像我們在瀏覽器中常見的那樣。
四窖壕、相關(guān)特性的模仿實現(xiàn)
對于上文中提到的相關(guān)特性忧勿,我寫了一個demo,對它們進行了簡要的模仿實現(xiàn)瞻讽。當(dāng)然簡書官方的實現(xiàn)會考慮到方方面面鸳吸,而我的demo僅是從Hybrid架構(gòu)的思想出發(fā),盼能夠拋磚引玉速勇。
這是demo中對該頁面的模仿實現(xiàn):
1.頁面初始化
在demo中层释,使用一條web頁面的URL來初始化VC:- (instancetype)initWithURL:(NSURL *)URL;
這條URL對應(yīng)文章的鏈接。
頂部導(dǎo)航欄和底部工具欄都是系統(tǒng)原生的UINavigationBar
和UIToolBar
快集,按鈕素材使用阿里巴巴的iconfont字體贡羔。
2.點擊作者頭像進入個人主頁
關(guān)于這個特性的實現(xiàn),如果按照 Hybrid 架構(gòu)的思想个初,屬于 Web 頁面調(diào)用原生方法乖寒,進入一個原生的VC。點擊頭像院溺,JS腳本執(zhí)行相關(guān)代碼楣嘁,調(diào)用原生方法暴露出來的接口,執(zhí)行原生方法珍逸。
我在這里用一種簡要的方法實現(xiàn):原生代碼利用之前提到的代理方法逐虚,在用戶點擊頭像后,攔截該URL谆膳,分析URL為頭像部分叭爱,直接執(zhí)行原生方法跳轉(zhuǎn)到個人主頁VC。
通過分析簡書文章頁面的網(wǎng)頁源代碼漱病,我發(fā)現(xiàn)用戶頭像對應(yīng)的URL
中的Query
部分买雾,有一個參數(shù)為utm_medium=note-author-link
把曼。據(jù)此,在- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
代理方法中加以判斷漓穿,若是頭像鏈接嗤军,則跳轉(zhuǎn)到個人主頁VC。下面是相關(guān)代碼:
NSURL *destinationURL = request.URL;
NSString *URLQuery = destinationURL.query;
// 簡書點擊文章中頭像時跳轉(zhuǎn)至原生頁面晃危。此處利用頭像鏈接中的一個參數(shù)作判斷
if ([URLQuery containsString:@"utm_medium=note-author-link"])
{
NSLog(@"我跳轉(zhuǎn)到個人主頁啦");
AvatorViewController *avatorVC = [[AvatorViewController alloc] init];
[self.navigationController pushViewController:avatorVC animated:YES];
return NO;
}
最后返回NO是因為若是頭像鏈接叙赚,該web頁面是不需要做跳轉(zhuǎn)操作的。
這里順便講一個小tips:如果想要在Mac端查看移動端的網(wǎng)頁源代碼僚饭,那么你只需要在Safari中輸入該頁面纠俭,并且在
開發(fā)
選項下的用戶代理
中,選擇iOS系統(tǒng)下的Safari作為代理浪慌,這時再使用源代碼查看冤荆,看到的就是移動端的網(wǎng)頁源代碼了。
3.點擊評論按鈕权纤,頁面滑動到評論區(qū)域
這個特性的實現(xiàn)方式和上面類似钓简,點擊評論按鈕,原生代碼構(gòu)建一條JS語句汹想,交由- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
方法進行執(zhí)行外邓,由web頁面執(zhí)行滑動操作。代碼如下:
- (void)scrollToCommentField
{
[self stringByEvaluatingJavaScriptFromString:@"scrollTo(0,20500)"];
}
這里的JS語句非常簡單古掏,由于筆者的前端知識還有所欠缺损话,沒有想到可以精確滑動到評論區(qū)域的JS語句,所以簡要實現(xiàn)槽唾,點到為止丧枪。
4.原生組件寫評論,web頁面更新
這里首先需要貼一下文章頁面的網(wǎng)頁源代碼:
<!-- 評論列表 -->
<div data-vcomp="comments-list" data-lazy="3">
<script type="application/json">
{"likedNote":true,"commentable":true,"publicCommentsCount":3,"noteId":2491941,"likesCount":43}
</script>
</div>
可以看到庞萍,頁面的評論內(nèi)容是異步加載的拧烦。所以這個功能的實現(xiàn),我
認(rèn)為比較合理的邏輯是原生組件向服務(wù)器提交一條新的評論钝计,收到成功回調(diào)之后恋博,原生組件和web頁面進行交互,執(zhí)行更新并加載評論列表的JS代碼私恬,從而看到自己發(fā)的新評論债沮。
5.內(nèi)外站頁面的區(qū)分
這里和點擊頭像的實現(xiàn)方法類似,通過攔截鏈接的URL本鸣,區(qū)分內(nèi)部鏈接和第三方鏈接疫衩,從而在開始加載的時候采用不同的加載界面,或者對于第三方鏈接單獨開啟一個第三方VC永高。
demo中關(guān)于第三方鏈接的關(guān)閉按鈕的顯示邏輯隧土,做出了相應(yīng)的處理提针。
五命爬、demo中的不足
看到這里大家應(yīng)該就會發(fā)現(xiàn)曹傀,對于提到Hybrid我們就會想到的Bridge、Router等模塊饲宛,我并沒有做明顯的限定皆愉。這樣也是為了方便大家用一種更接近以往原生代碼編寫的思維,來理解Hybrid模式艇抠。
同時幕庐,demo中較多涉及了原生代碼對web頁面做出的溝通操作。而沒有JS代碼對原生代碼的調(diào)用家淤,這是因為一來站在一個簡書客戶端的用戶和iOS開發(fā)的角度异剥,對于JS端執(zhí)行的操作,有些力不能及絮重,這本是和你共同工作的前端伙伴的任務(wù)冤寿,二來對于一篇幫助大家入門Hybrid的文章來說,從這個單方面的交互來入手青伤,管中窺豹督怜,已是足夠。
六狠角、一些感悟
其實号杠,寫了這么多,我覺得收獲到一些感悟是最重要的丰歌,下面的要講的姨蟋,可能是我覺得更為重要的思想性的東西。
1.未來的趨勢之一立帖,便是大前端團隊進行客戶端開發(fā)芬探。
- 看到這里你發(fā)現(xiàn),如果你們的團隊想要采用Hybrid模式進行產(chǎn)品的開發(fā)厘惦,光靠iOS或者是安卓的客戶端工程師是不可能完成的偷仿。在客戶端框架的開發(fā)過程中,需要和前端的工程師溝通具體的技術(shù)細節(jié)宵蕉。比如怎樣設(shè)計接口能夠更好地兼顧客戶端和前端特點酝静,對于某個問題,如何能把握全局而不是單單從客戶端的角度來看待羡玛。這些可能是普通的iOS開發(fā)工程師和大牛的差距所在之一别智。
- 越來越多的客戶端工程師招聘要求中,出現(xiàn)了熟悉前端語言的要求稼稿。如果你能在精通客戶端開發(fā)之余薄榛,對前端語言也游刃有余讳窟,那么在接下來的發(fā)展趨勢中,就會有更多的可能性敞恋。所以丽啡,請開始你的前端學(xué)習(xí)吧~。
2.在Hybrid模式下硬猫,如何進行產(chǎn)品技術(shù)方案的取舍
- 如前文所見补箍,簡書客戶端對于內(nèi)部域名的內(nèi)容和第三方內(nèi)容,在展示方式上是有明顯不同的啸蜜。在閱讀簡書的文章時坑雅,讓用戶發(fā)現(xiàn)不了自己是在一個瀏覽器上進行閱讀,這在方方面面就極大改善了用戶體驗衬横。為了做到這一點裹粤,我推測簡書首先需要對自己的內(nèi)容進行非常良好的CDN加速,以保證內(nèi)容加載時不會耗時過長蜂林,同時采取一些預(yù)加載策略遥诉,二是在內(nèi)容加載時,采用與原生界面部分相同的loading界面悉尾,去掉進度條突那,模擬原生界面的加載過程。而對于第三方的鏈接构眯,采用
進度條+返回愕难、關(guān)閉按鈕
的設(shè)計,則更符合用戶在瀏覽器中進行閱讀的習(xí)慣惫霸,也可以和自己的內(nèi)容進行直觀區(qū)分猫缭,這也改善了用戶體驗。 - 對于某些原生和Web頁面都可以實現(xiàn)的特性如何取舍壹店,這也是需要考慮的問題猜丹。比如,點擊評論按鈕頁面滑動硅卢,這個功能使用web頁面的滑動而非原生的控制顯然更為自然也更符合用戶習(xí)慣射窒。而對于撰寫評論的功能,使用原生的鍵盤将塑、編輯器組件脉顿,當(dāng)然就比使用web頁面的鍵入更加穩(wěn)定可控了。
七点寥、文章的demo
對于文章內(nèi)容艾疟,我寫了一個demo,這是demo地址。
為了便于理解蔽莱,我為代碼寫了詳盡的注釋弟疆。
如果覺得它對你有幫助,不妨在github上為我點一個star~非常感謝盗冷!