前段時間用 ionic 開發(fā)了一個 App,當(dāng)時需要調(diào)用 WebView 來顯示一些內(nèi)容(本來采用的 iframe 方案,由于很多網(wǎng)站已經(jīng)禁用了 iframe 且這種方式不太安全可靠,后來想想還是改用 WebView 了)。WebView 下有很多問題啊,一是樣式和 ionic 不太匹配,而是性能上顯得奇慢,突出表現(xiàn)為耗費大量流量而且加載慢。樣式匹配的問題有現(xiàn)成解決方案,本篇主要針對當(dāng)時遇到的性能問題給出一些建議。
H5 頁面加載慢的原因
原因有兩個方面:
- 自身渲染速度慢,比如解析 js 腳本,還有手機或者其他硬件設(shè)備的性能
- 如果 js 腳本過于復(fù)雜,前端又包含大量 js 腳本的話,會影響渲染速度
- 手機等硬件問題就不說了,尤其是 Android 手機
- 資源加載慢,主要是因為 H5 頁面請求數(shù)量太多(包括 js、css 等一系列資源文件)
- 基本 url 請求
- css、js 甚至圖片視頻等資源文件
- 這些請求是串行的,不請求完不行
一些解決辦法
- Android WebView 自身有一個緩存機制
- H5 頁面的預(yù)加載
Android WebView 緩存
緩存的好處有兩個:
- 加載過后沒有網(wǎng)絡(luò)連接時也能訪問
- 提高訪問速度并減少流量損耗
Android WebView 緩存機制其實也就是 H5 頁面的緩存機制,主要有5種:
-
瀏覽器緩存
瀏覽器緩存主要依賴 Http 頭中 Cache-Control (Expires)和 Last-Modified(Etag) 等字段來控制
Cache-Control:文件在本地緩存的時間
Expires:同 Cache-Control 一樣,前者優(yōu)先級更高,是 http1.1 協(xié)議之后增加的字段
Last-Modified:文件在服務(wù)器上最新更新時間,當(dāng)本地緩存過期時,發(fā)送 If-Modified-Since 字段帶上時間給服務(wù)器,服務(wù)器判斷文件是否更新過,沒有更新服務(wù)器會返回304,有更新返回200,并返回最新更新的文件
Etag:功能同 Last-Modified
常用做法:Cache-Control 與 Last-Modified 一起用,Expires 和 Etag 一起使用
優(yōu)點:不需要你過多考慮,只要在協(xié)議上設(shè)定好
缺點:一是必須要首次加載之后,二是緩存被清之后還要加載,三是加載別人的網(wǎng)頁時協(xié)議不由你設(shè)定啊
場景:加載靜態(tài)資源時,可以考慮,如 JS、CSS、圖片等
實現(xiàn):不需要你考慮,WebView 自帶瀏覽器自動實現(xiàn)
-
Application Cache 緩存
Appliaction Cache 是一種以文件為緩存,且文件有一定更新機制的緩存機制,是專門用于 WebApp 緩存的機制。在 HTML 文件頭部用 manifest 說明,是瀏覽器緩存的一種補充
優(yōu)點:同上
缺點:同上
場景:同上
實現(xiàn):通過設(shè)置 WebView 的 settings 來實現(xiàn)。
WebSettings settings = getSettings(); String cacheDir = context.getFilesDir().getAbsolutePath()+"cache/"; settings.setAppCachePath(cacheDir); settings.setAppCahceMax(50*1024*1024); settings.setAppCacheEnabled(true); -
Dom Storage 緩存
Dom Storage 通過 key-value 的形式來存儲字符串,分為兩種:
- sessionStorage:臨時性,頁面關(guān)閉后無法使用
- localStorage:持久性,頁面關(guān)閉后仍能使用
其存儲空間對于不同瀏覽器不同
優(yōu)點:相對于 cookie 來說,存儲空間更大,不用和 cookie 一樣每次都向服務(wù)器發(fā)送請求。存在本地,相對安全穩(wěn)定
缺點:同上
應(yīng)用場景:取代 cookie 機制。存儲簡單、臨時的數(shù)據(jù)。
實現(xiàn):還是設(shè)置 WebView 的 settings
WebSettings settings = getSettings(); settings.setDomStorageEnabled(true); -
Web SQL Database 緩存
Web SQL Database 實質(zhì)上是一種基于 SQL 的數(shù)據(jù)庫存儲機制,適合于結(jié)構(gòu)化數(shù)據(jù)的存儲
優(yōu)點:充分利用數(shù)據(jù)庫特點,方便增刪改查
缺點:同上,且官方不再維護,取而代之是 Indexed Database 緩存機制
應(yīng)用場景:存儲結(jié)構(gòu)化數(shù)據(jù)
實現(xiàn):依然是設(shè)置 WebView 的 settings
WebSettings settings = getSettings(); String cacheDir = context.getFilesDir().getAbsolutePath()+"cache/"; settings.setDatabaseEnabled(true); -
Indexed Database 緩存
Indexed Database 屬于 NoSQL 數(shù)據(jù)庫,也是通過 key-value 的形式來提供的
優(yōu)點:存儲空間大,默認(rèn)推薦是250M存儲空間,可以生成索引,同樣可以像操作數(shù)據(jù)庫一樣操作數(shù)據(jù),采用異步的 API 調(diào)用,用戶體驗較好
缺點:同上
應(yīng)用場景:存儲復(fù)雜、結(jié)構(gòu)化的數(shù)據(jù)
實現(xiàn):都是設(shè)置 WebView 的 settings
WebSettings settings = getSettings(); settings.setJavaScriptEnabled(true); // what?? 只要設(shè)置支持 JS 就能自動打開 IndexedDB 存儲機制,神奇不神奇?
關(guān)于 Android WebView 自身緩存機制總結(jié)
我們可以根據(jù)不同的場景設(shè)置不同的緩存機制,也可以組合使用,通常的做法是醬的:
- 存儲靜態(tài)資源
- 瀏覽器緩存
- Application Cache
- 存儲臨時簡單數(shù)據(jù)
- Dom Storage
- 存儲復(fù)雜數(shù)據(jù)量大的數(shù)據(jù)
- IndexedDB Storage
H5 頁面預(yù)加載
可是以上方案都是第一次加載依然很慢咋辦?為了提升這個體驗,我們產(chǎn)生了 資源預(yù)加載 的方案
它是怎么實現(xiàn)的呢?具體的 App 有具體自己的方式
一種方式是通過攔截 H5 頁面的資源網(wǎng)絡(luò)請求,從而從本地讀取資源而不是從網(wǎng)絡(luò)讀取
一種實現(xiàn)就是,首先將頻率更新低、常用且固定的靜態(tài)資源文件放到本地,攔截 H5 頁面資源網(wǎng)絡(luò)請求,如果檢測到有相同的靜態(tài)資源,直接使用本地的,不再發(fā)送請求。
舉個栗子:很多微信就是采取這樣的方案來解決問題的,圖片和視頻等資源文件在 wifi 才加載或者最后才加載
Demo 可以參考 https://github.com/Carson-Ho/WebView_InterceptRequest
當(dāng)然,在我開發(fā) ionic App 的時候,沒法修改這些 WebView 設(shè)置啊之類的這么做,但是也想用預(yù)加載啊,怎么辦?沒事,預(yù)加載的沒有具體實現(xiàn),它只是一個思路,具體情況具體分析。
比如像我這樣的情況:ionic 沒法修改 WebView 設(shè)置,僅僅是能調(diào)用 WebView,我打開的 url 是外部 url,不是自己的網(wǎng)站,靜態(tài)資源和腳本是別人網(wǎng)站設(shè)置的不由我控制
我的解決方案(僅供參考):在 App 啟動時,當(dāng)加載完數(shù)據(jù)之后,后臺默默啟動一個 WebView ,提前將需要訪問的網(wǎng)站(第一頁假如有20個網(wǎng)頁)訪問一遍,使其在瀏覽器中留有緩存,當(dāng)我在瀏覽這一頁時,WebVIew 就把下一頁的網(wǎng)頁提前訪問一遍。醬就感覺快多了。