1. 程式人生 > >Hybrid app開發歷程分享

Hybrid app開發歷程分享

關於這個話題,本文並不準備詳述移動開發相關的一些通用技術,例如:viewport、rem、flexbox、媒體查詢等。這裡主要講述我們的hybrid產品策略、開發流程與規範、效能優化以及我們踩過的坑。而往往就是這些,網上相關的資料相對比較匱乏的,又缺少類似經驗文章,所以希望通過此篇文章,跟大家分享一些魅族團隊關於hybrid產品開發的經驗。

產品背景

  1. 我們希望這一類產品具備比較強大的運營能力;
  2. 介面資料來自於cp資料整合,可能會有一定的差異性,甚至需要一定的容錯能力;
  3. 要求能夠實現快速更新和迭代。

Android與H5整合方案

在以上背景下面,我們最終選擇了Hybrid App這個方案。那麼,接踵而來的問題就是,客戶端該如何訪問網頁前端資源?能不能就採用以前瀏覽器訪問伺服器網頁的方式呢?答案不是絕對的,當然也可以採用上述的方式。根據不同的業務需求,方式總是會有不一樣的。我們採用最多的是以下兩個方案:

靜態資源本地化

該方案,會先在app裡面內嵌一份靜態資源,然後在使用者啟動app或者訪問html頁面並且符合一定條件的情況下,會發送請求去後臺,詢問資源的情況。如果發現有更新,那麼就把最新的資源下載到本地,然後接下來使用者訪問的都是本地的靜態資源了。如果更新失敗,那麼他訪問的就是內嵌的或者已經下載好的靜態資源。

HTML 5 應用程式快取

這個方案,其實就是採用HTML5的應用程式快取(HTML5 Cache Manifest )。在這裡就不詳述了,網上關於這個的資料很多。值得注意的點,就是manifest的配置檔案,需要配置正確的MIME-type,即 "text/cache-manifest" 。

一般是在開發一些活動或者專題頁面(上線時間短,ui變化多端)的時候,採用第二個方案。

而大多數的app頁面都是相對穩定,或者說是迭代開發,定期更新的,具備規律性,這種情況下,我們採用的是第一個方案。採用這個方案的時候,我們需要嚴格控制資原始檔的大小,除了必要的壓縮之外,還可以把長期固定的資源提取出來,提前內建到app裡面。那麼,使用者每次更新資源時,只需要去伺服器獲取變化的部分,大大減小了流量使用。

以上兩個方案,相對於每次都訪問伺服器靜態資源來說,具有明顯的優點:

  1. 離線訪問
  2. 資源載入快
  3. 伺服器負載低

頁面載入

首先,客戶端載入某頁面,載入完成後(pagefinish),客戶端會呼叫前端提供的初始化方法(initParams)。通過該方法實現引數的傳遞和頁面的初始化。

資源構建

我們目前使用fis3進行資源的壓縮、合併、新增 md5 戳等構建功能,對於使用了es6的專案,還需要用到Babel。在此基礎上,我們還開發了一些內部使用的基於fis3的外掛。最終的目的,就是為了實現一條命令列,完成全部構建工作。

開發環境搭建

搭建本地web服務,使用的是nodejs+express。

開發流程


從上圖我們可以看出,視覺設計、前端開發、客戶端開發、後臺開發都在很大程度上實現了並行開發。

接下來,簡單描述一下前端開發怎麼實現和後臺,還有和客戶端解耦,然後實現並行開發的。

首先,介面文件(後臺介面文件和客戶端介面文件)是不阻塞的前提。有了後臺介面文件,我們就可以依此構建相應的假資料,並且模擬相應的介面請求。而有了android介面文件後,我們也可以模擬呼叫客戶端介面,至少保證了基本的邏輯是順暢的。所以,只要有了介面文件,在進行到真正的聯調之前,前端、後臺、客戶端這3者都是獨立開發,互不阻塞的。

然後聯調的話分三個階段:

模擬假資料聯調

這個階段的話其實只需要編寫一些假資料在本地,然後用ajax請求就行了。而android介面的呼叫,也是模擬呼叫便可。在這一階段,主要是為了確保前端邏輯基本跑通。

後臺真實介面呼叫

到了這已階段,我們需要的是訪問本地靜態資源,呼叫的卻是遠端伺服器的介面。此種情況下,主要是要解決ajax的跨域請求問題。實現方式也挺多的,最簡單的就是設定一下瀏覽器支援跨域。當然,你也可以使用nginx或者apache的反向代理來實現ajax的跨域請求。

三方聯調

這一步,已經到了提測前的最後一個步驟。這個時候,客戶端已經和靜態資源整合,然後呼叫的介面,無論是android介面還是後臺伺服器介面,都不再是模擬的了。

3種聯調狀態的切換,無論是在開發、bug定位、頁面除錯等情況下,都是經常需要使用的。比如你在修改某個js邏輯bug的時候,首先,你一般都是從第二種聯調步驟開始的,畢竟在pc瀏覽器上面除錯bug還是要方便挺多的。然後,等到bug改得差不多了,才會把靜態資源push到手機上面,進行真機除錯。

接下來就產生了一個問題,如何實現這3種聯調狀態的靈活切換呢?我們來看一段程式碼:

2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <!-- dev start --> <script src="json/android-test.js"></script> <script type="text/javascript"> detail.getUrl = 'json/filmDetail.json'; detail.filmShopsUrl = 'json/filmShops.json'; android.initParams({ cityName: '珠海', lat: '1.23232', lng: '2.32324', id: 111, fromFilmDetail: true }); android.stopLoading = function(json){ android.doAndroidAction('stopLoading', ''); }; // detail.reloadShopList("{\"cityName\": \"珠海\", \"movieId\": 111,\"countyCode\": \"132ddd\"}"); </script> <!-- dev end --> <!-- min start --><!— 真實程式碼 --><!-- min end -->

為了實現3種聯調狀態的切換,我們基於fis3開發了一個外掛,這個外掛的功能也很簡單,就是處理上面這段程式碼。

如何處理呢?首先看一下上面這段程式碼,它可以分為兩個模組,第一個是模擬程式碼模組(用<!-- dev start -->和<!-- dev end -->包起來),第二個是真實程式碼模組(用<!-- min start --><!—和--><!-- min end -->包起來)。模擬程式碼模組包括了一個模擬android介面的js檔案引入、假資料的路徑以及模擬android呼叫初始化介面。真實程式碼模組可以寫一些真正上線需要使用到的路徑。這兩個模組都不是必需的。

外掛需要做的,就是把模擬程式碼模組去除,把真實程式碼模組釋放。

再來說說這樣做的好處,在模擬假資料聯調的階段,不需要使用到該外掛,那麼此時根據上面的那段程式碼,執行的就是模擬程式碼模組。而一旦我們要進行真正的介面聯調的話,就需要在fis3的parser階段呼叫該外掛,以實現去除模擬程式碼並使用真實程式碼。而外掛的使用,是在fis3的配置檔案裡面進行配置的,通過fis3的media api,我們可以實現動態控制外掛是否使用。

效能優化和坑

這個是一個比較大的話題,像那些大家已經耳熟目染的雅虎十四條等頁面優化準則和資料,那是非常之多。我在這裡,會盡量從我們的專案角度出發,列舉部分我們所做的優化和解決的一些難題。

圖片

能不用的情況下,儘量不用。非得用的話,可以從多個角度進行優化。比如:

  1. 放在cdn,讓資源離使用者近一點,同時也減少了cookie等非必要資料的傳輸。
  2. 採用CSSSprites減少請求量。
  3. 使用WebP減少圖片體積。
  4. 小的icon可以使用base64:URL圖片。
  5. 有些情況,你是沒辦法從技術角度進行優化的,可能需要在儘量不影響使用者體驗的情況下,調整ui或者產品方案。比如ui同學設定了一個不規則模糊漸變的背景圖片。那麼這種情況下,就不得不通過保留模糊漸變,但是讓其有規律性,這樣可以通過擷取小部分的圖片,然後通過平鋪的方式來實現了。有時候不得不做這樣的權衡,選擇一個折中的方案。

viewport

關於視口寬度的設定,目前比較通用的兩種方式:

  1. 寫死固定的寬度
  2. 設定為裝置寬度

第一種方式,簡單方便,不需要媒體查詢,不需要rem等,一個版本,適配所有機型。而且關於頁面精細度的問題,當視口寬度設定的比較大的時候,線條、字型等可以實現更加精細化的控制。

關於第二種方式,目前大部分大公司的移動端都是採用這種方式的。這種方式效能要比上一種高,畢竟少了一個縮放的過程,然後相容性方面也是比較好的。

所以經過上述比較後,我們推薦第二種方式的。但是我們也發現,在和一些其他公司的人溝通過程,他們由於各種原因,還是採用了第一種方式。同時,我們也有一些專案,也是採用的第一種方式實現的。

而在採用第一種方式的情況下,我們發現,有些時候頁面載入的時候,繪製效果不是很好,會有一個從左到右平鋪的過程(其實就是一個縮放的過程)。關於這一點,其實是可以通過opacity的控制來優化效果的。

動畫

動畫這一塊,想必就是Hybrid app的一個痛點。想要在android內建的webview裡面,實現和android差不多效果的動畫,其難度實在是太難了,而且還會遇到挺多坑的。

比如,在做影院訂座頁面的時候,座位的選擇區域是要能夠進行縮放的。有了這個功能需求後,我們就要開始開發了。

關於縮放,我們首先想到的就是css3裡面transform屬性的scale值。接下來,我們就採用了scale方案進行開發。

動畫開發的歷程總是坎坷的,果然,問題出現了。選座區域的座位,在進行了手動放大後,變得相當的模糊。

也許你會想,是不是由於採用了圖片,然後圖片進行放大變得模糊,那也是情理之中的。好的,那麼接下來,我們嘗試著,直接使用css設定背景顏色的方式進行座位繪製。但是,這樣做的結果是,放大的時候,還是模糊了。

在這種情況下,我們就需要靜下心來思考一下了。到底是為什麼呢。

在描述原因之前,先要引入另外一個古老的css屬性zoom,並分別講一下這兩者的區別:

  1. zoom一開始只是ie瀏覽器的私有屬性,後來大部分的瀏覽器都相容了該屬性的使用,但是畢竟還是沒能寫入規範。而scale則是寫入w3c標準的。
  2. 渲染順序不同。scale先宣染後縮放,zoom縮放後進行渲染。
  3. scale相關的控制引數較多,比如縮放原點、縮放方向等。而zoom預設的縮放原點就是左上角,然後也沒有直接的引數能進行縮放原點的修改。

根據上面列舉的幾點區別,我們可以得出以下結論:

  1. 使用zoom進行縮放,視覺效果會變得銳利。而採用scale進行縮放,視覺效果會變模糊。
  2. 使用zoom進行縮放,可能會引起頁面重排。而採用scale進行縮放,不會引起頁面重排。
  3. 使用zoom進行縮放,受限於html渲染規則,比如字型最小是12px的話,你再怎麼縮小,字型還是12px。而scale則不會。

接下來講一下座位放大後變模糊這個問題應該怎麼解決。根據以上結論,得出兩個解決方案:

  1. 使用zoom進行縮放,這樣就可以解決放大模糊的問題。
  2. 使用scale進行縮放,但是第一次的時候scale值設定得大一點,比如3(3倍大小)。然後再偷偷(setTimeout之類的)的把scale設定為1倍。

關於動畫的優化,就先講這一個列子,但是實際上,在我們的hybrid app開發過程中,還有很多的優化歷程,當然也踩了很多的坑。有興趣要了解的,可以試用一下魅族的生活服務,活動中心,電話黃頁等app,這些都或多或少的使用了hybrid方案進行開發。

結語

目前前端技術日新月異,我們在不斷的嘗試一些新的技術(react、es6等),不斷的緊扣一些細節,不斷的優化再優化。這一切,不是短短一篇博文所能講完,後續我們會有新的內容加入進來一起討論學習。