前段時(shí)間用 ionic 開發(fā)了一個(gè) App举农,當(dāng)時(shí)需要調(diào)用 WebView 來顯示一些內(nèi)容(本來采用的 iframe 方案府瞄,由于很多網(wǎng)站已經(jīng)禁用了 iframe 且這種方式不太安全可靠,后來想想還是改用 WebView 了)婉宰。WebView 下有很多問題啊貌笨,一是樣式和 ionic 不太匹配耳奕,而是性能上顯得奇慢衅斩,突出表現(xiàn)為耗費(fèi)大量流量而且加載慢盆顾。樣式匹配的問題有現(xiàn)成解決方案,本篇主要針對當(dāng)時(shí)遇到的性能問題給出一些建議矛渴。
H5 頁面加載慢的原因
原因有兩個(gè)方面:
- 自身渲染速度慢,比如解析 js 腳本惫搏,還有手機(jī)或者其他硬件設(shè)備的性能
- 如果 js 腳本過于復(fù)雜具温,前端又包含大量 js 腳本的話,會影響渲染速度
- 手機(jī)等硬件問題就不說了筐赔,尤其是 Android 手機(jī)
- 資源加載慢铣猩,主要是因?yàn)?H5 頁面請求數(shù)量太多(包括 js、css 等一系列資源文件)
- 基本 url 請求
- css茴丰、js 甚至圖片視頻等資源文件
- 這些請求是串行的达皿,不請求完不行
一些解決辦法
- Android WebView 自身有一個(gè)緩存機(jī)制
- H5 頁面的預(yù)加載
Android WebView 緩存
緩存的好處有兩個(gè):
- 加載過后沒有網(wǎng)絡(luò)連接時(shí)也能訪問
- 提高訪問速度并減少流量損耗
Android WebView 緩存機(jī)制其實(shí)也就是 H5 頁面的緩存機(jī)制,主要有5種:
-
瀏覽器緩存
瀏覽器緩存主要依賴 Http 頭中 Cache-Control (Expires)和 Last-Modified(Etag) 等字段來控制
Cache-Control:文件在本地緩存的時(shí)間
Expires:同 Cache-Control 一樣贿肩,前者優(yōu)先級更高峦椰,是 http1.1 協(xié)議之后增加的字段
Last-Modified:文件在服務(wù)器上最新更新時(shí)間,當(dāng)本地緩存過期時(shí)汰规,發(fā)送 If-Modified-Since 字段帶上時(shí)間給服務(wù)器汤功,服務(wù)器判斷文件是否更新過,沒有更新服務(wù)器會返回304溜哮,有更新返回200滔金,并返回最新更新的文件
Etag:功能同 Last-Modified
常用做法:Cache-Control 與 Last-Modified 一起用色解,Expires 和 Etag 一起使用
優(yōu)點(diǎn):不需要你過多考慮,只要在協(xié)議上設(shè)定好
缺點(diǎn):一是必須要首次加載之后餐茵,二是緩存被清之后還要加載科阎,三是加載別人的網(wǎng)頁時(shí)協(xié)議不由你設(shè)定啊
場景:加載靜態(tài)資源時(shí),可以考慮忿族,如 JS锣笨、CSS、圖片等
實(shí)現(xiàn):不需要你考慮肠阱,WebView 自帶瀏覽器自動實(shí)現(xiàn)
-
Application Cache 緩存
Appliaction Cache 是一種以文件為緩存票唆,且文件有一定更新機(jī)制的緩存機(jī)制,是專門用于 WebApp 緩存的機(jī)制屹徘。在 HTML 文件頭部用 manifest 說明走趋,是瀏覽器緩存的一種補(bǔ)充
優(yōu)點(diǎn):同上
缺點(diǎn):同上
場景:同上
實(shí)現(xiàn):通過設(shè)置 WebView 的 settings 來實(shí)現(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:臨時(shí)性簿煌,頁面關(guān)閉后無法使用
- localStorage:持久性,頁面關(guān)閉后仍能使用
其存儲空間對于不同瀏覽器不同
優(yōu)點(diǎn):相對于 cookie 來說鉴吹,存儲空間更大姨伟,不用和 cookie 一樣每次都向服務(wù)器發(fā)送請求。存在本地豆励,相對安全穩(wěn)定
缺點(diǎn):同上
應(yīng)用場景:取代 cookie 機(jī)制夺荒。存儲簡單、臨時(shí)的數(shù)據(jù)良蒸。
實(shí)現(xiàn):還是設(shè)置 WebView 的 settings
WebSettings settings = getSettings(); settings.setDomStorageEnabled(true);
-
Web SQL Database 緩存
Web SQL Database 實(shí)質(zhì)上是一種基于 SQL 的數(shù)據(jù)庫存儲機(jī)制技扼,適合于結(jié)構(gòu)化數(shù)據(jù)的存儲
優(yōu)點(diǎn):充分利用數(shù)據(jù)庫特點(diǎn),方便增刪改查
缺點(diǎn):同上嫩痰,且官方不再維護(hù)剿吻,取而代之是 Indexed Database 緩存機(jī)制
應(yīng)用場景:存儲結(jié)構(gòu)化數(shù)據(jù)
實(shí)現(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)點(diǎn):存儲空間大串纺,默認(rèn)推薦是250M存儲空間丽旅,可以生成索引,同樣可以像操作數(shù)據(jù)庫一樣操作數(shù)據(jù)纺棺,采用異步的 API 調(diào)用榄笙,用戶體驗(yàn)較好
缺點(diǎn):同上
應(yīng)用場景:存儲復(fù)雜、結(jié)構(gòu)化的數(shù)據(jù)
實(shí)現(xiàn):都是設(shè)置 WebView 的 settings
WebSettings settings = getSettings(); settings.setJavaScriptEnabled(true); // what?? 只要設(shè)置支持 JS 就能自動打開 IndexedDB 存儲機(jī)制祷蝌,神奇不神奇办斑?
關(guān)于 Android WebView 自身緩存機(jī)制總結(jié)
我們可以根據(jù)不同的場景設(shè)置不同的緩存機(jī)制,也可以組合使用,通常的做法是醬的:
- 存儲靜態(tài)資源
- 瀏覽器緩存
- Application Cache
- 存儲臨時(shí)簡單數(shù)據(jù)
- Dom Storage
- 存儲復(fù)雜數(shù)據(jù)量大的數(shù)據(jù)
- IndexedDB Storage
H5 頁面預(yù)加載
可是以上方案都是第一次加載依然很慢咋辦乡翅?為了提升這個(gè)體驗(yàn)鳞疲,我們產(chǎn)生了 資源預(yù)加載 的方案
它是怎么實(shí)現(xiàn)的呢?具體的 App 有具體自己的方式
一種方式是通過攔截 H5 頁面的資源網(wǎng)絡(luò)請求蠕蚜,從而從本地讀取資源而不是從網(wǎng)絡(luò)讀取
一種實(shí)現(xiàn)就是尚洽,首先將頻率更新低、常用且固定的靜態(tài)資源文件放到本地靶累,攔截 H5 頁面資源網(wǎng)絡(luò)請求腺毫,如果檢測到有相同的靜態(tài)資源,直接使用本地的挣柬,不再發(fā)送請求潮酒。
舉個(gè)栗子:很多微信就是采取這樣的方案來解決問題的,圖片和視頻等資源文件在 wifi 才加載或者最后才加載
Demo 可以參考 https://github.com/Carson-Ho/WebView_InterceptRequest
當(dāng)然邪蛔,在我開發(fā) ionic App 的時(shí)候急黎,沒法修改這些 WebView 設(shè)置啊之類的這么做,但是也想用預(yù)加載啊侧到,怎么辦勃教?沒事,預(yù)加載的沒有具體實(shí)現(xiàn)匠抗,它只是一個(gè)思路故源,具體情況具體分析。
比如像我這樣的情況:ionic 沒法修改 WebView 設(shè)置汞贸,僅僅是能調(diào)用 WebView绳军,我打開的 url 是外部 url,不是自己的網(wǎng)站矢腻,靜態(tài)資源和腳本是別人網(wǎng)站設(shè)置的不由我控制
我的解決方案(僅供參考):在 App 啟動時(shí)门驾,當(dāng)加載完數(shù)據(jù)之后,后臺默默啟動一個(gè) WebView 踏堡,提前將需要訪問的網(wǎng)站(第一頁假如有20個(gè)網(wǎng)頁)訪問一遍猎唁,使其在瀏覽器中留有緩存咒劲,當(dāng)我在瀏覽這一頁時(shí)顷蟆,WebVIew 就把下一頁的網(wǎng)頁提前訪問一遍。醬就感覺快多了腐魂。