公司想要將自己的產(chǎn)品由網(wǎng)頁端移植到原生,提升用戶體驗,因此在網(wǎng)上查找相關資料。最后App決定使用Hybrid架構來實現(xiàn)快速上線。
第一次寫文章,比較粗糙,諸位看官如果想更細致的了解HybridApp,請移步最下參考資料。
文章部分引用了相關資料的語句,在此感謝相關資料的作者。
伴隨著移動互聯(lián)網(wǎng)產(chǎn)業(yè)的興起,各式App層出不窮,我們迫切需求一種更快速、成本更低、技術更為成熟的項目開發(fā)方案,而H5的成熟讓我們看到了希望,使得Web App開始崛起。
為了提高用戶體驗,目前網(wǎng)站一般分為3個版本,簡版、移動版、電腦版,簡板Web普遍是基于WAP2.0開發(fā)的,界面視覺效果差、功能簡單;移動版則普遍使用H5開發(fā),適用于現(xiàn)在大家普遍使用的移動上網(wǎng)設備,擁有更好的視覺效果、交互方式,迥然不同于簡板Web的設計風格;而電腦版在此則不做討論。
個人認為現(xiàn)在的Web App就是由移動版網(wǎng)頁發(fā)展而來。
目前App大致分為三個大類
-
Web App
定義:將所有功能都放在Web上展現(xiàn),運行基于本地瀏覽器。在此將給Web簡單的套一層App外殼的應用也歸入Web App。完全采用HTML/CSS/JS編寫,專為觸摸操作進行了優(yōu)化。目前iOS已禁止簡單的套殼App上架。
優(yōu)點:開發(fā)速度快,跨平臺,成本低,實時迭代用戶無需更新
缺點:網(wǎng)絡速度要求高、服務器壓力大,系統(tǒng)級別API調(diào)用難度大,用戶體驗差、用戶留存度低 -
Native App
定義::NativeApp是基于手機本地操作系統(tǒng)并使用原生語言編寫的 。因為位于平臺層上方,向下訪問和兼容的能力會比較好一些,可以 支持在線或離線訪問,消息推送或本地資源訪問,攝像撥號功能的調(diào) 取。但是由于設備碎片化,App的開發(fā)成本要高很多,維持多個版本 的更新升級比較麻煩,用戶的安裝門檻也比較高。
優(yōu)點:用戶體驗佳、交互風格與系統(tǒng)吻合,節(jié)省流量,可訪問本地資源,速度快,用戶留存度高
缺點:成本高,版本迭代慢,需要過審 -
Hybrid App
定義:介于Web App與Native App的一種折中方案,底層(框架)部分由iOS/Android開發(fā)人員處理,上層(內(nèi)容展現(xiàn))部分由Web前端人員處理,用戶界面操作邏輯及部分靜態(tài)資源駐留本地,使得Web App可以對操作迅速反應并在很大程度上實現(xiàn)離線訪問。Hybrid App追求趨近于原生App的體驗,但目前還較困難。

需要考慮的方面
- 分清Native與前端的界限,Native提供宿主環(huán)境,前端需要合理的利用Native提供的資源,提升用戶體驗及自身性能。在設計上需要考慮以下問題:
- 交互設計:如何設計與前端的交互?Native需要考慮提供NativeUI/Header/消息/Alert等組件接口、通訊錄/系統(tǒng)/設備信息讀取接口、Native/H5相互跳轉(zhuǎn)(H5跳Native、H5新開WebView跳轉(zhuǎn)、Native跳轉(zhuǎn)H5)等問題。
- 數(shù)據(jù)訪問:Native如何訪問H5資源(File方式訪問H5本地靜態(tài)資源/URL方式訪問服務器資源)。資源增量替換是Android的,iOS不用考慮。
- 登錄(及支付/分享等)模塊:這些模塊具體由誰實現(xiàn)?Native/Web一方操作怎樣使另一方收到對應信息并處理?(支付/分享建議Native端來做,都有相應的SDK)
- 開發(fā)調(diào)試:Native與前端需要商量出一套可開發(fā)調(diào)試的模型,不然很多業(yè)務開發(fā)的工作將難以繼續(xù)。
Hybrid交互設計
UIWebView由于其API難用、還有內(nèi)存泄漏,現(xiàn)在已經(jīng)棄用,iOS8以上都應該盡量用新的WKWebview。WK提供了一系列API來使得Native與Web的信息交換簡單高效。還有一個不可忽視的一點是WK使用與Safari相同的JS引擎+內(nèi)置手勢+無內(nèi)存泄漏,是UIWebView的替代者。所以在這里UIWebView只做簡要介紹。
-
UIWebView
Native調(diào)JS方法
webView.stringByEvaluatingJavaScriptFromString()JS調(diào)Native方法
UIWebView沒有辦法直接使用js調(diào)用app,但是可以通過攔截request的方式間接實現(xiàn)JS調(diào)用Native方法,另一種是使用JavaScriptCore的jsContext注冊objc對象或使用JSExport協(xié)議導出Native對象的方式。本文主要介紹第一種實現(xiàn),第二種實現(xiàn)方式不再贅述。
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool{
//如果請求協(xié)議是hello 這里的hello來自js的調(diào)用,在js中設為 document.location = "hello://你好";
//scheme:hello ,msg:你好
//通過url攔截的方式,作為對ios原生方法的呼叫
if request.URL?.scheme == "hello"{
let method:String = request.URL?.scheme as String!
let sel = Selector(method+":")
self.performSelector(sel, withObject:request.URL?.host)
request.URL?.path
//如果return true ,頁面加載request,我們只是當做協(xié)議使用所以不能頁面跳轉(zhuǎn)
return false
}
return true
}
函數(shù)回調(diào)/返回值
Native調(diào)JS可以有返回值,但是JS調(diào)Native是通過間接的攔截request方式實現(xiàn),它根本就不算方法調(diào)用,所以應該是不存在可以直接產(chǎn)生返回值的。當然如果需要Native對JS的調(diào)用有所響應,可以通過回調(diào)函數(shù)的方式回應JS??梢栽谡{(diào)用Native的時候增加一個JS回叫函數(shù)名 app在處理完之后調(diào)用回調(diào)函數(shù)并把需要的參數(shù)通過回調(diào)函數(shù)的方式進行傳遞。
- WKWebView
Web腳本注入
WKUserScript 允許在正文加載之前或之后注入到頁面中。這個強大的功能允許在頁面中以安全且唯一的方式操作網(wǎng)頁內(nèi)容。
WKUserScript 對象可以以 JavaScript 源碼形式初始化,初始化時還可以傳入是在加載之前還是結(jié)束時注入,以及腳本影響的是這個布局還是僅主要布局。于是用戶腳本被加入到 WKUserContentController 中,并且以 WKWebViewConfiguration 屬性傳入到 WKWebView 的初始化過程中。這個樣例可以簡單擴展為更為高級的頁面修改方法,例如去除廣告、隱藏評論等。
let source = "document.body.style.background = \"#777\";"
let userScript = WKUserScript(source: source, injectionTime: .AtDocumentEnd, forMainFrameOnly: true)
let userContentController = WKUserContentController()
userContentController.addUserScript(userScript)
let configuration = WKWebViewConfiguration()
configuration.userContentController = userContentController
self.webView = WKWebView(frame: self.view.bounds, configuration: configuration)
Native調(diào)JS方法
//直接調(diào)用JS
webView.evaluateJavaScript("hi()", completionHandler: nil)
//調(diào)用JS帶參數(shù)
webView.evaluateJavaScript("hello('liuyanwei')", completionHandler: nil)
//調(diào)用JS獲取返回值
webView.evaluateJavaScript("getName()") { (any,error) -> Void in
NSLog("%@", any as! String)
}
WK通過與UIWebView類似的方法調(diào)用JS語句,但獲取返回值的方式不同,WKWebView用的是回調(diào)函數(shù)獲取返回值。
**JS調(diào)Native方法**
Web中的信息也可以通過調(diào)用這個函數(shù)被傳給Native里:
```
var message = {
'method' : 'hello',
'param1' : 'haibao',
};
window.webkit.messageHandlers.webViewApp.postMessage(message);
//這個 API 真正神奇的地方在于 JavaScript 對象可以自動轉(zhuǎn)換為 Objective-C 或 Swift 對象。
//Native中Handler的注冊handler需要在WKWebView初始化之前
config = WKWebViewConfiguration()
//注冊js方法
config.userContentController.addScriptMessageHandler(self, name: "webViewApp")
webView = WKWebView(frame: self.webWrap.frame, configuration: config)
//處理handler委托。ViewController實現(xiàn)WKScriptMessageHandler委托的func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage)方法
//實現(xiàn)WKScriptMessageHandler委托
class ViewController:WKScriptMessageHandler
//實現(xiàn)js調(diào)用ios的handle委托
func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {
//接受傳過來的消息從而決定app調(diào)用的方法
let dict = message.body as! Dictionary<String,String>
let method:String = dict["method"]!
let param1:String = dict["param1"]!
if method=="hello"{
hello(param1)
}
}
```
**Alert攔截**
在WKWebview中,JS的Alert是不會出現(xiàn)任何內(nèi)容的,你必須重寫WKUIDelegate委托的runJavaScriptAlertPanelWithMessage message方法,自己處理Alert。類似的還有Confirm和Prompt也和Alert類似。
```
Alert攔截方法
func webView(webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: () -> Void) {
completionHandler()
let alert = UIAlertController(title: "ios-alert", message: "\(message)", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "ok", style: .Default, handler:nil))
alert.addAction(UIAlertAction(title: "cancel", style: .Cancel, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
```
Native/Web端做好交互格式約定,Native端提供規(guī)范性的API供Web端訪問。
常用API:
* 界面跳轉(zhuǎn)(N->W/W->N/W->W/W->W新開WebView)+過場動畫
* Header組件
* NativeUI相關組件(Loading遮罩等)
* 分享
* 推送
* 登錄
框架架構
架構選擇MVC,因為上層邏輯都是Web端實現(xiàn)的,對于Controller的負擔不是太重。
框架結(jié)構為TabBar+Navi。
Web部分將入口界面的樣式資源存放本地,后續(xù)跳轉(zhuǎn)及動態(tài)數(shù)據(jù)通過網(wǎng)絡獲取。
下圖是參考的Web目錄結(jié)構圖。

數(shù)據(jù)請求
約定調(diào)用請求格式
let data = {url:'requestURL',
param: {參數(shù)},
type: 'post'
}
約定返回數(shù)據(jù)格式
let data = ["data": data, //網(wǎng)絡請求結(jié)果、本地數(shù)據(jù)等回傳信息
"errno": errno, //錯誤碼
"msg": msg, //描述
"callback": callback] //回調(diào)ID
Web端通過傳遞一個字典將需要的請求信息交給Native處理,Native處理完畢后數(shù)據(jù)通過Web端回調(diào)。
let dataString = self.toJSONString(data)
webView.stringByEvaluatingJavaScriptFromString(self.getRequest + "(\(dataString));")
本地數(shù)據(jù)訪問
約定數(shù)據(jù)訪問格式
let data = {name:'name',
type:'dataType'
}
約定返回數(shù)據(jù)格式
let data = ["data": data, //本地數(shù)據(jù)請求結(jié)果
"errno": errno, //錯誤碼
"msg": msg, //描述
"callback": callback] //回調(diào)ID
參考資料:
聊聊Web App、Hybrid App與Native App的設計差異
Hybrid App開發(fā)實戰(zhàn)
iOS 8 WebKit框架概覽
淺談Hybrid技術的設計與實現(xiàn)1
淺談Hybrid技術的設計與實現(xiàn)2
去啊App實戰(zhàn):極致的Hybrid混合式開發(fā)
三層架構
UIWebView和WKWebView的使用及js交互
WKWeb View