與webview打交道中踩過的那些坑

呂大豹發表於2013-11-07

  隨著HTML5被越來越多的用到web APP的開發當中,webview這一個神器便日漸凸顯出重要地位。簡要的說,webview能夠在移動應用中開闢出一個視窗,在裡面顯示html頁面,css以及js程式碼也可以被解析執行,它使用的是我們熟悉的webkit核心。android和ios都有相應的API,所以寫一份程式碼在多個平臺執行的能力就是以webview為基礎的。

  今天我們要聊的不是如何使用webview,而是筆者本人作為一名前端工程師,在與客戶端開發人員通過webview打交道中遇到的種種神奇事件。

  事情還得從去年說起,我還是一個小白實習生的時候。當經理知道有webview這個神器之後,遂下令讓android組和ios組削減工作內容,所有共同的介面均由web組提供。而web組當時處於“傳統軟體公司無前端”的局面,頁面相當臃腫,壓根不適於移動裝置。於是乎,不明局勢的經理指揮一個不明真相的小白實習生,帶著還沒使利索的jQuery,開始了所謂的Hybrid App開發。一個悽慘的故事拉開序幕,下面是在開發當中踩過的各種坑,記錄下來以供備忘。

webview與裝置自帶瀏覽器一樣嗎?

  webview會呼叫系統自帶的瀏覽器核心來解析頁面。這是個真命題嗎?市場上的大部分平板電腦自帶瀏覽器為webkit核心,webview使用的也是webkit核心,並且按一個應用的大小來看也不可能自己帶一個核心進去。所以是呼叫系統自帶的是沒錯的了。那我在一臺裝置上,使用瀏覽器開啟一個頁面和使用webview開啟同一頁面,得到的結果會是一模一樣嗎?當然是廢話了,都說了是呼叫關係。

  假如真是廢話,那我也沒必要記下這一點了。因為從我的實際操作情況來看,有些時候確實是不一樣的。瀏覽器裡一個樣,webview裡又是一個樣。ipad上情況好些。在android品牌雜亂的裝置上,此現象還真的出現不只一次。尤其是頁面DOM結構比較複雜的時候。

  有理由懷疑我的程式碼不符合W3C規範啥的,但業界良心,W3C規範還是不敢違反的。所以得出結論,webview與自帶瀏覽器解析的結果並不是完全一致,不能以為頁面在瀏覽器中正常了,在移動裝置上也就正常了。

絕對要慎用的瀑布流

  大概是兩年前,瀑布流這個概念紅遍大江南北,各網站紛紛效仿,相應的文章、jQuery外掛層出不窮。後來真正有思想的人開始質疑,提出我們要學習的不是瀑布流的形式,而是思想,我們需要的是真正適合自己產品的東西。後來,我們經理也聽說了瀑布流這個東西。。。

  加!加瀑布流!我們不要分頁列表那些東西!於是小白實習生網上各種搜尋,找到了當時比較流行的叫做infinitescroll的jQuery外掛。各種改原始碼配合實現產品業務、各移動裝置相容性bug處理暫且放一邊。這裡要提的是與移動應用密切相關的一個問題。

  做移動開發的對閃退這種現象估計是咬牙切齒,移動應用的效能問題一直是不容小覷的。就webview來講,當html頁面的DOM元素很多,或者說層級關係較複雜時,對其的壓力是相當大的。再看看我們的瀑布流,隨著頁面的滾動,不斷往上append節點,這對webview來說壓力極大,當節點數量到一定程度時,就發現頁面滾動不是那麼流暢,開始一卡一卡。別急,你在翻轉一下螢幕試試,瞬間崩潰,介面這個花了,內容像是繪製不出來一樣。因為在做橫豎屏翻轉時,解析引擎會進行頁面的重繪,這麼多的節點工作量可不小。ipad因為其優越的影象處理效能表現還不錯,android裝置上簡直一塌糊塗。

  有什麼解決辦法呢?我在聽一個大牛的經驗分享會上曾聽到,在頁面滾動的時候可以通過計算,動態remove節點,保證使用者能看到的地方是有內容的,其他滾動捲去的部分就直接remove掉,等滾動回來的時候再加回來。這樣保證頁面上的節點不會太多,效能自然提升。我沒有嘗試這種方案,其中的注意事項也不好說。不過大牛成功了,必然是可行的。

  總之得出一個結論,在移動裝置上,要用瀑布流,一定要慎用,必須先有效能的解決辦法。

響應式佈局與viewport

  我們知道移動裝置在渲染頁面的時候,會先在一個虛擬畫布上渲染,然後再縮放到裝置的尺寸,比如IOS是在寬度為980px的虛擬畫布上渲染。我們看一些響應式設計的文章,也會知道在頁面<head>中要新增如下內容:

<meta name="viewport" content="width=device-width, initial-scale=1.0"/>

  來保證頁面會按照裝置的寬度進行渲染而不是使用虛擬畫布。然後便可以使用響應式設計的相關技術,彈性盒子、媒體查詢等,讓頁面適應裝置寬度顯示。

  然而我遇到了一個問題,因為頁面結構較複雜,在橫豎屏翻轉的時候出現了花屏,各種顯示不全,各種抽風抖動。當然是在android裝置上。。。原因就是裝置的寬度發生變化webview要進行頁面重繪,然而在重繪的過程中,由於頁面太複雜而不堪重負,繪到一半不管了。

  因為當時該頁面的設計,會顯示圖片、音訊、視訊等媒體,並且是多個同時顯示,進行頁面精簡不可行。說來慚愧,面對緊迫的時間,我只好悄悄把上面的<meta>標籤刪掉,讓頁面還是在虛擬畫布上渲染,這樣渲染好的頁面在進行橫豎屏翻轉的時候,貌似是不會進行重繪的,只會由系統縮放一下,花屏的現象也不會發生了。只不過在豎屏下,頁面元素明顯小了。算是個下策。

  像這樣的情況,個人覺的在頁面設計的時候就應該考慮到,若要進行橫豎屏翻轉,頁面儘量設計的精簡清爽。不過話說回來,移動應用上的頁面,精簡是一直需要且必須的。

儘量不要用別人的外掛

  不怕丟人的承認,我們做web App用的是jQuery,原諒我當時是個小白吧。。。如果早些知道我們的東西只需支援現代瀏覽器,無論如何也得試試zepto或是其他的小而輕的庫。不過既然用了,還是來面對這個現實吧。

  小白的特徵之一就是從素材網站收藏了好多jQuery外掛,然後在專案中不假思索就用。我們的頁面是先在PC上用,然後才被告知要被webview引用的。這下麻煩來了,原先使用的好好的外掛,一但跑在移動裝置上,各種羊癲瘋發作。然後開始不遺餘力的改原始碼,過程簡直不堪回首。

  比較典型的一個,我們的頁面中有富文字編輯器,當時選擇的是國內的一款開源編輯器KindEditor。我沒有詆譭這個專案的意思,它在PC上使用還是蠻好的。一上了平板,直接傻了,基本上廢了。環視網上,沒有一款為移動裝置設計的編輯器。所以編輯器這東西,還是讓原生程式碼來做比較合理。

  另一個外掛用的比較痛苦的是H5視訊播放的,用了mediaelement,它在官網宣稱支援各瀏覽器各平臺,然而真正效果卻並非宣稱的那樣。在android4.0以上的裝置中都有各種相容性問題。又是一頓改原始碼。。。

  所以得出的結論就是,如果某個元件可以自己寫出來,千萬別從網上找別人的,到頭來自己還得麻煩。倘若真的準備要使用,一定先做一個全方位的測評。包括能否與你的業務邏輯完美融合,能否支援你所需要的裝置。

:hover偽類在移動裝置上的特殊現象

  我們在做滑鼠懸停效果時,經常會用到:hover,無論用在<a>標籤或是其他標籤,現代瀏覽器都能正常相容。比如我有一個圖示,在滑鼠移上去之後想要一個陰影效果,可能會這樣寫:

.icon:hover{box-shadow:2px 2px 2px;}

  在移動裝置上,是沒有滑鼠指標的。當用手指點選圖示的時候,可以出現陰影效果,這種效果也是可以接受的。但是當點選完成後,手指離開了螢幕,圖示的hover效果卻沒有消失,依然是帶著陰影,就好像是有一個隱形的滑鼠指標停留在圖示上一樣,實在是讓人不能理解。當再點選頁面的其他部分時,圖示的hover效果就消失了,好像是隱形的滑鼠指標移到了別的地方。

  ipad和android裝置上都有此現象。為了避免此問題,css中的:hover偽類就必須利用媒體查詢,只在桌面瀏覽器中生效。

ipad關閉螢幕造成的問題

  ipad出於節約電量的設計,關閉螢幕後瀏覽器中的一些執行緒也會暫時關閉,等到開啟螢幕時再起。如果有需要持續執行的js程式碼,關閉螢幕後便無法工作了。比如,要用setInterval函式實現一個計時功能,每隔一秒進行時間更新。當螢幕關閉後,計時函式就不能工作了,即關閉螢幕5秒種,你的計時器也將停止5秒。

  如果是實現一些網頁動態效果,這倒沒什麼影響。但如果你的計時器涉及到了業務邏輯,比如計的是一次考試的時間,那影響就大了。一個考生關閉螢幕後將可以使時間靜止。這是不希望發生的。所以,如果程式碼中有用js進行計時,或其他需持續執行的任務。需要考慮到此問題。

  對於計時器,我們可以隔一段時間與伺服器進行時鐘同步,從而解決客戶端因關閉螢幕造成的計時誤差。

  順便再加一句,使用ipad上的safari瀏覽器,在切換到別的標籤頁時,原標籤中的執行緒也會被停掉,跟關閉螢幕一樣的效果。

定位屬性與重繪的糾葛

  在移動裝置橫豎屏翻轉的時候,會進行頁面的重繪,ipad影象處理效能較強,問題不大,但是形形色色的android裝置效能不一,一些較差的在轉屏時經常會顯示不出來介面,或是出現花屏。比較明顯而且嚴重的情況是,當頁面上的元素使用了position:fixed或是position:absolute時,轉屏後該元素的定位將會錯亂,可能是轉屏時頁面所處的環境變動太大,渲染引擎計算的時候效能消耗太多,總是無法將這類元素正確顯示出來。

  我的處理辦法是自己讓webview把這個元素再重繪一下,操作元素的屬性、className,或者是設定visibility均可觸發重繪行為。但這種方式也不是屢屢見效,有時候也無濟於事。沒辦法,android就是這麼奇葩,太不穩定。所以實在不行我也會這麼寫:

$(‘html’).css(‘visibility’,’hidden’);
setTimeout(function(){
         $(‘html’).css(‘visibility’,’visible);
},100);

  我懷疑webview的重繪是否真的進行了,所以直接讓頁面閃爍一下,這樣效果會好很多,一般情況都能重繪正確。

  再糟的情況,實在用HTML解決不了了,可以去找客戶端的同學,讓他們操作webview,webview也可以進行重繪或者是直接reload。

不得不說的touch事件

  在桌面瀏覽器上,基本的功能都是靠監聽click事件來完成的,移動裝置上沒有滑鼠指標壓根就沒有click這事件,不過對於這麼重要的東西,引擎當然是做了相應處理。click事件在pad上可以很好的被響應。

  但正如上面提到的,小白總是會使用一些現成的外掛。。我用了一個彈出視窗外掛,在桌面上可以用滑鼠拖動,但是在pad上卻無法拖動了。移動裝置上的引擎雖然對click做了很好的處理,但是對mousedown、mousemove、mouseup卻沒有理會,所以需要在程式碼中對應的加上touchstart、touchmove、touchend事件。

  有些現象是可以想出原因來的,但有些現象是那種根本就不講道理的,我這裡也不知道如何描述。大概就是當touch事件和click事件同時被監聽,再加上頁面結構複雜等其他因素,就會出現各種抽風行為。

  首先精簡頁面結構是最最重要的,沒有之一。其次對於不同裝置的奇異現象,我也只能想辦法進行各種hack了。

明確要支援哪些裝置

  這一點跟技術無關了,是一個決策問題。ipad無論是2、3、4還是mini都比較穩定,相容問題基本沒有。主要是android,系統版本從2.x到4.x不等,各種牌子:三星、華碩、聯想算比較主流的,其他是像昂達、酷派、粵教雲。而且各品牌還有多種型號。再加上各廠家對android系統的無節操修改,瀏覽器核心都改。整個現在那叫一個亂啊。

  所以在專案初期,明確要支援哪些裝置是非常重要的。這樣就可以針對這些裝置進行相容性的測試。而不是今天客戶說要用哪個裝置,我們就想辦法相容哪個裝置,真是會累死。還真有這樣的客戶,說我們就只使用千元以下的平板,效能那真是一個蛋疼。

 

  經歷了一番痛苦的掙扎,秉著能解決就解決,解決不了就hack的原則,專案還真按期完成了。hack寫多了真是有種犯罪感啊!現在看著專案,真的有種假如上天給我再來一次機會的吶喊,不過一切都過去了。經驗教訓一大把,在此挑些典型的與大家共享。可能在專做移動開發的前端眼裡,這些都是小兒科,但作為一個從小白走過來的程式設計師,這些經驗還是相當重要。

相關文章