前端頁面熱更新
了解過前端性能優化的同學應該清楚,給頁面加載提速的終極方案就是CDN,這是BS架構本身的特點決定的,無論什么前端提速手段,最終都會回到客戶端文件的傳輸上來;與之相對的CS架構則不存在加載壓力,但CS架構的問題是更新不靈活,那么有沒有一種方法能結合這兩種架構的優點,在加載速度和更新靈活性之間找到一個平衡點呢?這就是本文要探討的一種方案:前端熱更新。
方案概述
“前端”和“熱更新”這兩個詞通常很少一起出現,提到熱更新一般都是指APP的一種靜默更新方式,這種方式會在用戶使用時悄悄檢測并下載增量更新包,當用戶下次打開APP時自動應用更新,從而將APP“更新”這個破壞連貫性的動作隱藏于無形;前端頁面的加載則相當于每次都是“全量更新”,如果能讓前端頁面也能用上“本地模板”,那將極大縮短前端加載時間,而且以此為前提,我們也可以實現一個前端的模板熱更新機制,做到不影響頁面更新的實時性。
應用場景
場景一:APP內嵌頁面。
比如電商類APP的首頁,經常需要改版或者做活動皮膚,如何減少更新成本就成了一個大問題。使用了熱更新方案我們就可以用HTML實現APP首頁,頁面內容以模板的形式存進,后臺靜默更新模板,下次啟動自動生效;針對具有一定時效性的活動皮膚,我們以補丁的形式發布,補丁文件疊加在模板上產生最終的活動模板效果,對于補丁包我們可以提前加載并預存在本地,補丁包應該包含自身的生效時段信息,前端檢測到時間處于活動周期內時應用補丁。最終可以做到熱更新頁面無論改版還是做活動,只需要前端發版就可以,完全不需要APP端參與。
場景二:追求加載速度的web頁面。
對于web頁面來說更新不是問題,加載才是最大的問題,如果個別頁面希望極致提升頁面展現速度,那么也可以使用該方案作為提速手段,但因為頁面的所有代碼都將存進,所以不適合大范圍使用。
需求細化
綜合以上場景和需求,最終我們要做的東西是一個“殼”頁面,該頁面沒有具體業務內容,只實現熱更新功能,每次加載都先檢查中是否存在模板,如果有則立即應用模板,此時頁面展現出來,如果沒有則進入下一步;下一步頁面會請求模板管理接口獲取最新模板信息,拿到模板信息后如果本地已有模板,則與本地模板比對版本信息,如果版本一致說明緩存命中,流程結束;如果本地版本不是最新,則獲取最新模板并存進本地,下次頁面加載時將應用最新的模板,流程結束;另一種情況是首次加載本地沒有任何模板,那么將獲取最新模板本地網站模板修改,保存到本地,然后應用模板,流程結束。
前面說的是穩定模板的更新流程,穩定模板流程結束后會進入補丁模板更新流程。首先仍然是檢查本地是否存在補丁模板,如果已存在則檢測當前時間是否匹配補丁的生效時段,匹配則應用補丁,不匹配將進入下一步;下一步將獲取最新補丁模板并存到本地,然后檢測當前時間是否匹配最新補丁的生效時段,如果匹配則應用模板,不匹配流程結束。
完整流程如圖所示:
實現細節接口數據
根據功能需求我們需要接口返回穩定模板信息和活動模板信息本地網站模板修改,分別都包含id和url兩個字段,id用于版本校驗,url指向模板文件下載地址,活動模板信息還需要額外提供cycle字段,定義活動模板的生效時段,與之相對的我們還需要接口返回服務器當前時間,用于匹配活動模板的生效時段,最終完整的數據結構如下:
{ "status": "Y", "data": {

"stableVersion": {
"id": "17",
"url": ""
},
"activeVersion": {
"id": "18",
"url": "",
"cycle": "2018,02,01-2018,02,10"

},
"today": "2018,02,06" }
}
本地數據
保存到本地的數據大致跟接口數據保持一致,只保留和信息,字段在id和url基礎上再增加用于保存模板字符串,完整本地數據結構如下:
{ "stableVersion": {
"id": "17",
"url": "",

"template": "" }, "activeVersion": {
"id": "18",
"url": "",
"cycle": "2018,02,01-2018,02,10",
"template": "" }
}
模板文件
前端頁面由三種語言構成,但我們希望只用一次請求就把模板文件拿到,所以模板是一個包含了html/css/js的文本文件,標簽格式就保持普通HTML文件的寫法,考慮到模板應用部分的實現,需要約定一下標簽的寫法,例如css必須用標簽包裹,js必須用標簽包裹,這樣一來用正則表達式就很容易提取到各部分代碼段。
模板應用
如上段所說,獲得模板文件后可以使用正則表達式拿到三種語言代碼,然后只需要按照css > html > js的順序依次將他們插入頁面相應位置,就完成了模板應用,唯一不同的是html代碼將以的方式覆蓋進body元素。在應用順序上,將css放在html之前是為了避免重繪,將js放在html之后是為了能夠在js中操作DOM。
活動模板雖然定義為補丁,但模板構成跟穩定模板其實是相同的,應用方式也完全相同,只不過由于活動模板在穩定模板之后應用,所以活動模板的css和js都將以補丁的方式影響頁面,對于普通的換皮膚需求只需要css和js就足夠了,但如果希望html也能發生一些改變,根據html的覆蓋式應用方式,活動模板中就需要給出一份完整的html代碼,以達到修改html的目的。
效果展示
后記
整個方案的流程比較瑣碎,但實現過程其實很簡單,部署成本也不高,只需要后端把模板管理起來,再提供一個更新接口就行了,但這套更新機制還是有一個小問題,那就是當有新版本發布時用戶并不能第一時間看到新版本,必須下次訪問才能更新到新版本,這算是靜默更新要付出的一點點代價吧,如果實在介意這個問題其實也容易解決,只需要在檢測到遠程有新版本時提示用戶重啟/刷新就可以了。
相比較HTML5的緩存方案,我認為靈活性要更高一些,但不足之處在于不支持靜態文件的碎片化管理,但擴展這個功能也不復雜,無非模板信息里再擴展幾個字段而已。
代碼在這里了,更細節的東西自己看代碼吧: