內部Hybrid App經驗解讀

鄭昀編纂 關鍵詞:Hybrid,Zepto,Fastclick,Backbone,sui,SPA,pushState,跨域,CORS
  1. click 事件仍是 tap 事件?
  2. Zepto 的 show/hide 有時不靠譜
  3. Android 下的跨域問題
  4. pushState 調用失敗也屬於跨域問題

   內部作 Hybrid App 開發歷程也不短了,楊海波、潘軍和劉勤紅與前端組、App 組一塊兒總結了很多寶貴的經驗教訓,逐步造成咱們本身的標準打法。下面選了幾個經驗點(坑)作進一步解讀。
 

0x00,click 事件仍是 tap 事件?

300 毫秒點擊延遲的前因後果一文所言『儘管蘋果公司創造的雙擊縮放行爲,是一種在移動設備上訪問桌面端站點的不錯的解決方案,但隨之引入的 300 毫秒點擊延遲也成爲了移動端網站讓用戶感受卡頓的罪魁禍首之一』,此文把緣由和對策講得清楚透徹,此處鄭昀再也不贅述,感興趣的同窗自習一下。
 
那麼,咱們看到,時至今日,有三種應對措施:
  1. 對於無需縮放的頁面,禁用雙擊縮放功能,
  2. 引入 Zepto 框架的 tap 事件
    • Zepto 趁着 JQuery 在移動互聯網市場歷史包袱重的機會,搞了一套輕量級類 JQuery 框架的代碼,核心代碼千行,從而迅速成爲移動端 DOM 操做庫的首選。
    • Zepto 提供了一個 touch 庫,在載入 Zepto 後爲 document 綁定 touchstart、touchmove、touchend 事件,根據手指 x、y 值的位置,判斷方向從而觸發 tap、doubleTap、swipeLeft 等事件。
    • 本來沒有 tap 事件,Zepto tap 是用 touchstart 和 touchend 模擬出來的。
  3. 引入 Fastclick 的 click 事件
    • FastClick 是 FT Labs 專門爲解決移動端瀏覽器 300 毫秒點擊延遲問題所開發的一個輕量級的庫。簡而言之,FastClick 在檢測到 touchend 事件的時候,會經過 DOM 自定義事件當即觸發一個模擬click 事件,並把瀏覽器在 300 毫秒以後真正觸發的 click 事件阻止掉。
 
但咱們在實踐過程當中,也領教了著名的  Zepto tap 『點透』坑
對此,yexiaochai  分析道
1,一旦引入 touch 庫便會在全局綁定事件,每次點擊皆會觸發無心義的 tap 事件 ;
2,zepto 爲了實現 doubleTap 等功能,2B 地在 touchend 上設置了一個 setTimeout,而後整個世界都充滿了翔了。
因爲 setTimeout 的拋出主幹流程,致使其 event 參數失效,這個時候就算在 tap 事件函數中執行 e.preventDefault() 或者什麼都是無效的,這是致使 zepto tap「點透」的罪魁禍首。
點透的效果能夠看  http://www.cnblogs.com/lilyimage/p/3740668.html 所述。總之,有人 建議若是你還打算繼續用 Zepto,那麼它的 tap 事件已經沒用了,那你能夠本身 build 一個無 touch 模塊的 Zepto,以便減少文件大小並提升運行效率。
Fastclick 是 怎麼作的呢?
Fastclick 將事件綁定到你傳入的元素 上,在 touchstart 和 touchend 後(會手動獲取當前點擊 el),若是是類 click 事件便手動觸發 dom 元素的 click 事件。因此 click 事件在 touchend 便被觸發,因而整個響應速度就起來了。這裏雖然使用了 touch 事件,但 touch 事件是綁定到了具體 dom 而不是 document 上 ,因此 e.preventDefault 是有效的,咱們能夠阻止冒泡,也能夠阻止瀏覽器默認事件。
 
最終咱們採用 Fastclick 的 click 事件來規避點擊延遲響應。
 

0x01,Zepto 的 show/hide 有時不靠譜

在 Hybrid App 開發初期,咱們引入了 Zepto 的 fx_methods 模塊,來控制元素的顯示與隱藏:
fx_methods   Animated show, hide, toggle, and fade*() methods.
但老是出現一些莫名奇妙的問題,如元素不能正常顯示,元素的定位發生變化等,很是很差定位,最終前端同窗發現是 Zepto 的 show/hide 形成的。
對此,咱們能夠找到 2012 年的一個 issue 佐證:
When you add fx_methods to your Zepto, the show() and hide() methods are overriden with methods that also call animate. 
Even in the case of undefined speed (normal show), animate still adds many CSS properties to the shown/hidden nodes.
These unexpected CSS rules can interfere with the page CSS (in my case, it has changed the positioning of my child fixed DIVs).
即,她認爲,即便是簡單的顯示,fx_methods.js 的 show/hide 也會加入一些額外的 CSS,以致於影響整個頁面樣式,並且還會亂彈琴地改變透明度,重置爲 1.0。samwu 在 2013 年也 講過相似問題
因此,咱們有兩種選擇:
  1. 繼續引入 Zepto 的 fx_methods,但按 madrobby 所說,沒有定義 speed 時,僅調用原生的 show;
    • 從 Zepto 官方的  改成 
  2. 如 0x00 和本小節所描述的問題,不要引入 Zepto 的 touch 和 fx_methods 模塊
    • 只引入 zepto+event+ajax+form+ie 基本模塊
 

0x02,Android 下的跨域問題

在 Hyrbid App 裏,鄭昀要求採用 Template+Data=HTML 模式在手機客戶端本地渲染出所需的頁面。
Template(HTML5 模板文件+JS+CSS+Images)既能夠提早打包到 App 安裝包裏,也能夠從 CDN(如無,則溯源到靜態文件服務器)拉到客戶端裏並存儲在本地。
Data 則是經過模板文件裏的 JavaScript 腳本,從遠端拉取 JSON 格式的數據包。
那麼,這裏存在一個跨域問題:
  • Android 的 WebView 經過 file:// 協議加載本地的 HTML5 模板文件。本地文件的 JS 向遠端發送 AJAX 請求,受到同源策略的限制,譬如你可能會看到「XMLHttpRequest cannot load file://…… Origin null is not allowed by Access-Control-Allow-Origin.」的錯誤提示,即當你的 URI 是 file:// 時,源域名(origin)爲空。
    • 借用 difcareer 的評論:Web 應用程序能且只能使用 XMLHttpRequest 對象向其加載的源域名發起請求,而不能向任何其餘域名發起請求,但 HTML5 容許 AJAX 跨域向其餘域名發起請求,可是不能獲取服務器端響應
  • iOS 下無此跨域問題。
 
對此,大體有四種應對措施:
  • 若是是 GET 請求:
    1. 能夠經過 JSONP 來解決跨域問題;
  • 若是是 POST 請求:
  1. 由 App 原生代碼發起網絡操做,成功後回調 JS。
  2. 利用 HTML5 的「Cross-Origin Resource Sharing(CORS,跨域資源共享)」新特性,在服務器端響應頭裏設置:Access-Control-Allow-Origin 頭域;
    • 不限制來源,Access-Control-Allow-Origin: *
    • 限制來源域,Access-Control-Allow-Origin: http://xxx.com
    • 具體設置參考 http://enable-cors.org/
    • 這須要瀏覽器支持;
    • SmdCn 說起,『在某些版本的 Android 瀏覽器中,由於緩存的緣由,第一次 CORS 正常,但第二次會失效。對此,能夠經過在響應頭中增長 Cache-Control: no-cache 來解決』。
  3. 設置 Android 的 WebView 以下 settings 參數爲 True: html

    if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.JELLY_BEAN){ 前端

        webView.getSettings().setAllowUniversalAccessFromFileURLs(true); node

    } git

    由此容許經過  file url 加載的 Javascript 能夠訪問其餘的源,包括其餘的文件和 http/https 等源。這個設置在 JELLY_BEAN 之前的版本默認是容許,在 JELLY_BEAN 及之後的版本中默認是禁止的。
    According to the documentation for setAllowFileAccessFromFileURLs and setAllowUniversalAccessFromFileURLs, they used to be set to true for ICS and older. Starting in JellyBean they turned it off. If you are trying to load a local JS file from a local HTML page, then you'll need to enable these.  – Steven
 
咱們是既會在 Native API 裏封裝了一個接口 sendpostmsg,讓 JS 調用(但也所以致使『 除了 POST 請求,其餘均可以在 PC 瀏覽器調試』),也會使用 setAllowUniversalAccessFromFileURLs 方法。
 

0x03,pushState 調用失敗也屬於跨域問題

咱們須要使用 HTML5 的特性 history.pushState,手動插入歷史記錄和修改地址欄,這樣雖然地址欄被修改了,但並不觸發網頁跳轉。
同上節,本地文件的 JS 調用 history.pushState 也會遇到跨域問題,報錯以下圖所示:
緣由還是當你的 URI 是 file:// 時,源域名(origin)爲空。
因爲咱們使用了  SUI,繞不開這個問題。因此,最終仍是得調用 webView.getSettings().setAllowUniversalAccessFromFileURLs(true);
完全解決各類跨域問題。
 
參考資源:
2,2015,freebuf,鳶尾, 同源策略詳解及繞過(Part1)
4,2012,csdn, CORS(跨域資源共享)簡介
6,2011,sof, WebView Javascript cross domain from a local HTML file
7,2014,龔廣, WebView跨源攻擊分析
歡迎訂閱個人微信訂閱號『老兵筆記』,請掃描二維碼關注:
老兵筆記訂閱號二維碼
轉載時請註明「轉載自旁觀者-博客園」或者給出本文的原始連接。
-EOF-
相關文章
相關標籤/搜索