1. 程式人生 > >提升HTML5的效能體驗系列之一 避免切頁白屏

提升HTML5的效能體驗系列之一 避免切頁白屏

提升HTML5的效能體驗系列之一 避免切頁白屏

白屏 切換頁面 效能 轉場

提升HTML5的效能體驗系列文章目錄導航:
- [提升HTML5的效能體驗系列之一 避免切頁白屏]
- 提升HTML5的效能體驗系列之二 列表流暢滑動
- 提升HTML5的效能體驗系列之三 流暢下拉重新整理
- 提升HTML5的效能體驗系列之四 使用原生UI(nativeUI)
- 提升HTML5的效能體驗系列之五 webview啟動速度優化及事件順序解析
- 提升HTML5的效能體驗系列之六 降低記憶體佔用

最後更新時間:2017年10月7日。

窗體切換白屏的現實問題

HTML5的效能比原生差很多,比如切頁時白屏、列表滾動不流暢、下拉重新整理和上拉翻頁卡頓。
在低端Android手機上,很多原生App常用的功能和體驗效果都很難使用HTML5技術模擬。
我們首先來看第一個問題,如何避免切頁白屏。

瀏覽器的頁面在切換時,由於其頁面載入機制,在跳轉到下一個頁面時,先要請求聯網、載入頁面程式碼、構建dom、渲染,最後才顯示出來。
在最終結果渲染完畢前,會出現幾十毫秒甚至數秒的白屏。原生App是沒有這個問題的。
雖然使用SPA單頁應用模型,即ajax+div切換也可以避免白屏,但把所有頁面寫在一個SPA頁面裡,簡單幾個頁面還行。但頁面多了手機上也跑不起來,初始化非常慢,首頁必然白屏,而且工程大了程式碼那個亂。。。被坑過的人自然知道。

解決窗體切換白屏的方案

標準HTML5無法解決,我們就使用擴充套件的手段。
HTML5+是一套增強HTML5的規範,它可以用JS呼叫幾十萬原生API。
想要解決切頁白屏這個問題,需要使用plus.webview類來做MPA多頁應用(不是SPA單頁應用)。
plus.webview類是對原生的webview物件的js化封裝,使用js可以操作webview。
解決白屏的原理是:把每個頁面當作一個webview,但用js來控制它就像控制div一樣。動畫時通過原生的view動畫飄webview進來而不是通過css動畫飄div進來


同時webview之間相互獨立,不會出現SPA下不同頁面js和css衝突的問題。

通過操作webview來避免切頁白屏,有幾種常見的做法:

1、動畫先飄不會白屏的原生title進來

既然webview載入慢,轉場動畫會白屏。原生view載入快,不會白屏。那麼能不能使用原生view呢,或者至少動畫時先飄一個原生view的title進來,也不會整屏白屏。
HBuilder7.2起,提供了plus.nativeObj.View,也就是原生的view物件(以下簡稱nview),可以使用js向原生的view直接寫字、繪圖(注意是原生view不是webview)。
從HBuilder8.8起,優化了nview和Webview的關係,為Webview引入了titleNView和subNView,是從屬於Webview的原生介面。titileNView也稱ntitle,進一步對title的原生化做了簡化了操作,在plus.webview的style裡,可以配置titleNView,如下示例:

plus.webview.create('new.html', 'new', {'titleNView':{'backgroundcolor':'#FFFFFF','titletext':'標題欄','titlecolor':'#FF0000'}});

這樣建立webview時,會自帶一個原生的title,文字、顏色、是否有返回箭頭、分割線這些都可以設定,見http://html5plus.org/doc/zh_cn/webview.html#plus.webview.WebviewTitleNViewStyles。還可以通過getTitleNView()方法得到一個nview物件,自由的向上面寫字、繪圖、處理點選響應。參考nview文件http://html5plus.org/doc/zh_cn/nativeobj.html#plus.nativeObj.View
如果只是簡單修改,比如修改title文字,也可以通過重設TitleNView的style來實現:
plus.webview.currentWebview().setStyle({titleNView:{titleText:'new text'}})

在mui框架中,進一步簡化封裝了mui.openWindowWithTitle()方法,參考http://dev.dcloud.net.cn/mui/window/#openWindowWithTitle

上面title有了,中間空白處可以先轉個plus.nativeUI.waiting的原生雪花或顯示載入中,這樣轉場時就不會飄白屏了。
一般本地頁面載入都很快,轉場動畫300毫秒結束時,頁面也渲染出來了。

另外提供幾個讓HTML頁面渲染快的方法
- 頁面渲染儘量不用js做,想要渲染快,就直接寫HTML和css渲染,js渲染的介面顯示更慢。
- 少用padding、margin,儘量寫簡單的程式碼,讓頁面一次渲染到位,而不是反覆觸發重繪。
- 減少圖片尺寸,不要使用背景圖(最常見的效能問題均來自於此)

理解了titleNView,我們再來看subnview。
同理,subnview也是原生渲染的view,它可用於更大面積的原生渲染。
在流應用裡的唯品會中,商品詳情介面的載入速度那麼快,就是因為使用了subnview。參考視訊http://v.qq.com/x/page/k05051mc143.html
一般業務有titleNView就夠了,追求極致體驗的業務可以使用subnview。
所謂追求極致,就是要求在100毫秒渲染,動畫期間就要完成聯網和渲染。即使原生應用,大部分業務也是在動畫完成後才渲染介面的。
使用subnview要在頁面裡大量通過js構造介面,不太直觀。HBuilder8.3.3起,新增了wap2app專案,其中引入了nview模板,新建一個nview檔案,可以使用類vue的方式開發,參考http://ask.dcloud.net.cn/docs/#//ask.dcloud.net.cn/article/12757。後續會把nview模板引入到5+應用開發中。

延伸:既然有nview,那是不是可以使用nview做完整介面,廢除webview?類似react native那樣。
DCloud一直遵循的是HTML5+的規範和理念,即不推翻HTML,而是在HTML做的不好的地方強化補足。
即使在動畫期間大量使用了subnview,但滾動後的完整的頁面仍然應該是HTML的。
這樣的解決方案即能滿足使用者體驗要求,又能相容好HTML5,是更好的解決方案。

早些年mui曾推薦使用過head和body分開載入的方案,此方案已廢棄,由這裡描述的原生title方案替代。

2、預載入html

既然HTML渲染慢,是否可以後臺預載入,需要使用時直接動畫進來?
當然是可以的。所謂預載,即後臺預載新頁面的HTML檔案及資源,使用時直接調出這個已經建立好的webview。
尤其是從一個列表頁面反覆開啟詳情頁面,僅僅是其中的資料不同,此時應該預載和複用詳情Webview。
在Hello mui裡有一個列表到詳情的最佳實踐示例,就是使用了這個思路,強烈建議大家在列表到詳情時研究使用這個示例。文章參考http://ask.dcloud.net.cn/article/12575

只要伺服器返回資料不拖後退,一樣可以實現100毫秒渲染,動畫期間完成聯網和渲染。

預載入,由於不顯示出來,並不會過多增加資源佔用。(同時顯示在螢幕上的webview不要超過3個,隱藏在後臺的webview不要超過10個)
如果是list轉到content,不同的item點選只是一個頁面,完全可以使用預載。
但如果頁面不同且較多,此時不建議預載太多Webview。後臺預載太多webview需要耗很長,會搶cpu,此時使用者如果在前臺顯示的Webview裡操作比如滾動列表,會感覺到卡。

mui框架的窗體函式封裝

mui框架為了簡化窗體管理的工作,把一些常用的窗體模型做了簡化封裝。
但對於複雜的窗體切換,仍需開發者搞明白上面提到的窗體切換原理。
mui的init方法,通過引數封裝了preload,這樣就可以方便的預載webview。
mui的openWindow方法,封裝了顯示waiting,載入新頁面,處理動畫,關閉waiting等工作。
mui的openWindowWithTitle()方法,封裝了原生title。
mui的back樣式控制,自動封裝了窗體的隱藏和關閉。
這些方法具體參考mui的js API

啟動後首頁的白屏

首頁是沒有預載入的概念的。
首頁的控制基本都在manifest裡進行。
有2個與啟動白屏有關的manifest設定。
1.launchwebview
在launchwebview裡可以配置首頁的titlenview,以及使用subnview製作tab。
這樣頂部和底部實際上是由原生引擎渲染的,可以迅速顯示。
參考文章:基於subnview模式的原生tab


  1. splashscreen
    啟動封面的圖片如何關閉是在manifest裡配置的。
    預設是在首頁的webview的loaded事件發生後關閉。但又提供了若干選項。
    不管你的首頁是白屏了還是覺得進入太慢了,都可以控制。
    在工程下manifest.json裡找到plus、splashscreen節點,這裡有event選項,可以配置是在哪個事件時close splash,預設是loaded,也可以配成titileUpdate、rendering、rendered。
    預設配置loaded事件是偏保守的,避免有的開發者首頁程式碼寫的不高效,導致白屏。
    如果你的首頁程式碼效率高、渲染快,則推薦配置成titileUpdate事件。

還有一種手動控制splash關閉的技巧,如果splashscreen節點下的autoclose設定為false,即手動,可以在首頁程式碼裡寫js控制封面圖片的消失時機。
此時在首頁合適的位置,比如說聯網結束或業務上的其他時間點,呼叫js關閉封面圖片,plus.navigator.closeSplashscreen();
但不管什麼方法,5+引擎的splash顯示時間不會超過6秒,如果6秒內開發者仍不能做到首頁渲染,那麼使用者會看到白屏。

關於如何優化啟動速度,可以參考這篇文章http://ask.dcloud.net.cn/article/571

5+動畫詳解

這篇文章詳細描述了5+提供的各種原生動畫的特點及優化技巧,是必讀文章http://ask.dcloud.net.cn/article/225

Android5的動畫花屏、分塊渲染解決方案

如果你遇到了相關問題,可以參考http://ask.dcloud.net.cn/article/12837

後記

不管使用哪種方法,都要注意一點,手機App的HTML頁面必須本身效能足夠高。
這是老生常談的問題,但現實中還是大量App因為這個問題而導致效能體驗出問題。
編寫乾淨整潔、一次渲染的頁面非常重要。
現在太多開發者在研究模式、框架,讓頁面渲染要經歷二次、甚至四五次重繪才能完成。在短短几百毫秒的動畫期間,這麼幹要不讓頁面卡、要不讓渲染慢。
減少js二次渲染很重要的就是減少使用js渲染頁面,頁面結構要用簡單樸素的HTML寫,js可以動態填充資料,但如果js動態繪製頁面主體,那渲染速度上不去。
減少css二次渲染,就是少用複雜的選擇器,少用padding、margin這些會二次修正頁面的css。
如果追求極致的話,那jquery、zepto這些框架也不要使用,手機上都是webkit引擎,直接寫document的api操作dom即沒有相容問題又沒有效率問題。