混合應用中的javascript實踐

烈日神巫發表於2016-06-28

混合應用(hybird app) 在幾年前便進入大眾視野,近來更是越發風生水起,深受人民群眾的喜愛。

概念

什麼是混合應用

混合應用(hybird app) 顧名思義,便是將web app與native app混合在一起,既享受html快速開發、快速版本迭代帶來的便利,也能使用原生app呼叫系統介面和第三方SDK的強大擴充套件能力。

混合方式

如同茴字有幾種寫法一般,構建混合應用也有不同的方式。其中一種是主要使用靜態html,用 phonegapcordova 加殼的方式打包成app。另一種則是小部分webview直接引入服務端渲染的html,其他功能都是原生app開發。
其中前一種方式最為出名的解決方案便是使用 angular.js + cordovaionic,而使用後一種方式的app則數不勝數。
但是無論使用哪一種方式,都面臨一個問題,html和app的互動。html自己不會動起來,本文將淺談javascript如何互動app和html。

互動

方法注入

常見的app和html互動有 使用url互相呼叫方法 兩種方式。
使用url 這種方式比較簡單,透過 location.href = 'url' ,即可將方法和引數傳遞給app,但是無法傳遞複雜的資料。
互相呼叫方法 這種方式則較為複雜,除了需要app端寫好呼叫的方法注入到 window 物件之外,還需要JavaScript暴露方法給app使用。以常見的評論互動為例:

// 點選“發表評論”,js需要呼叫app的評論框
$('.js-comment').click(function(){
  window.appMethod.comment();
});

// app端在使用者點選“傳送”按鈕時,再呼叫javascript的插入評論方法(我不會app開發,以下是虛擬碼)
function comment(){
  TEXTAREA.OPEN();
}
SUBMIT_BUTTON.CLICK(function(){
  NSString * result = [self.webView stringByEvaluatingJavaScriptFromString:@"window.jsMethod.comment()"];
})

其中 window.appMethod 這個方法,是一個從 iOS 和 android 方法中提取而來的方法,根據手機系統不同,使用不同策略,例如:

var window.appMethod = null;

var androidMethod = {
  comment: function(){
    window.android.comment();
  }
};

var iOSMethod = {
  comment: function(){
    location.href = 'ios://comment?'
  }
};


window.appMethod = iOS_DEVICE ? iOSMethod : androidMethod;

相比方法的注入點,策略處理,方法的呼叫時機更為重要。在js中呼叫一個不存在方法,會發生錯誤從而導致程式碼無法繼續向下執行。
比如進入頁面時,app需要告訴html一些登入資訊,以初始化點贊,收藏等元件。如果由app直接呼叫js方法去通知,那麼很可能頁面還沒載入完,而發生上面提到的錯誤。
所以好的時機是讓js主動去向app發起請求,示例:

// 不和諧:app直接通知js更新使用者登入狀態,可能會發生錯誤
eval('window.jsMethod.setUser();')


// 和諧:js主動去向app發起請求
$(function(){
  window.appMethod.getUser();
})

// app端在接收到getUser方法後,呼叫js方法(我不會app開發,以下是虛擬碼)
function getUser(){
  // 獲取user狀態,然後執行
  NSString * result = [self.webView stringByEvaluatingJavaScriptFromString:@"window.jsMethod.setUser()"];
}

引數傳遞

以上的示例中的方法並沒有進行引數傳遞,是為了留到這裡。
相比JavaScript,在android和iOS方法中傳遞引數顯得極為嚴格,除了要指定引數個數,還要指定引數型別。
雖然可以透過陣列的方式進行不定個數引數的傳遞,但是指定引數型別還是挺煩人的。所以建議始終使用 String 型別作為引數進行傳遞,複雜的json格式引數使用 JSON.stringify。使用url傳遞則需要對引數進行 encode 編碼。
以上面的發表評論為例:

# 本段程式碼使用了jquery

// js給app傳參
var androidMethod = {
  comment: function(params){
    window.android.comment( JSON.stringify(params) );
  }
};

var iOSMethod = {
  comment: function(params){
    location.href = 'ios://comment?'+$.param(params)
  }
};

... ... 

$('.js-comment').click(function(){
  var params = {
    "user_id": 30,
    "article_id": 958,
    "article_type": "news"
  };
  window.appMethod.comment(params);
});


// app給js傳參
SUBMIT_BUTTON.CLICK(function(){
   var params = {
    "comment_id": 5484,
    "comment_content": "我不會寫app,姑且用js寫虛擬碼"
  };
  NSString * result = [self.webView stringByEvaluatingJavaScriptFromString:@"window.jsMethod.comment( JSON.stringify(params) )"];
})

方法監聽

這裡的方法監聽指的是app對js方法的單向監聽。
因為需要app監聽的js方法,都是顯示的呼叫了app方法,或是跳轉到了一個url。呼叫app的方法自不待言,url的監聽卻有多種。
一種是a連結的 http GET 請求的監聽,多見於 下一篇文章 , 相關文章 等跳轉頁面的方法。一般來說不用帶引數。還有一種是自定義的 schema 監聽,使用這種方式一般是不帶或帶較少的引數,比如 myiOS://mymethod?

資源載入

把資源載入放到互動裡面,我也不知道合不合適。對於 第一種混合方式 來說,可以把更多的靜態資源放到本地,然後透過app介面載入。對於第二種,更多的還是從服務端渲染並載入資源。
對於一些互動類的資料,而非資源,既可以選擇存放在瀏覽器的localStorge中,也可以選擇存放於app本地檔案,這取決於哪一方進行操作運算了。

除錯

html的移動端除錯時很困難的,嵌入app的html除錯更是難上加難。因為js和app的方法相互呼叫,任何一方出錯,都會導致程式不按套路執行。
除了仔細的約定兩端的方法和檢查程式碼的錯誤外,還需要一個強力的工具。在這裡推薦使用 vConsole ,可以比較直觀的定位到錯誤。

vConsole 截圖

vConsole

其他

相容性

html在app中的相容性取決於app內嵌瀏覽器核心。iOS系統預設的瀏覽器是safari,而andriod系統預設的瀏覽器則五花八門。

如果選擇系統預設瀏覽器作為內嵌瀏覽器的話,ios一般相容性較好,能取得和PC端一致的效果。android機型則會比較悲劇,使用 crosswalk方案 或許是個正確的選擇。

特性啟用

在內嵌瀏覽器中,html5的某些特性預設是關閉的,比如 localStorge , 需要app端手動去開啟。所以在開發中一定要做好可用性檢測,減少出現bug的機率。

區域性檢視

當html作為webview的區域性檢視被載入時,由於app雖然可以獲取到頁面載入後的高度,但是無法獲取動態改變後的高度,使得區域性滾動失效。
所以在作為區域性檢視載入時,如果需要頁面高度會根據比如評論,動畫效果而發生改變的話,需要及時通知app更新webview高度。

相關文章