您好,登錄后才能下訂單哦!
這篇文章主要介紹怎么解決HTML5頁面無縫閃開的問題,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!
傳統方案的困境
無論是 html 離線,還是直出,以及讓 webview 啟動和網絡請求并行 ,頁面的切換和打開都無法避免 html 加載這一過程。對于大型應用而言,龐大的 js 初始化解析和執行會耗費巨大的時間。
新的思考方向?
速度優化的本質是以空間換時間。我們是否可以從這個思路,將打開 webview 及解析 html 這以過程省略掉呢?答案是可以的。
容器化方案
容器化
即是我們最終探索與實踐的出來的一套方案。正常 web 頁面關閉后,webview 組件即會銷毀掉,下一次再打開需要重新啟動。通常讓 webview 保持常駐的做法可以節省 webview 啟動時間, 但簡單的常駐 webview 并不能做到頁面秒開,頁面打開仍然需要重新解析 html。
對于我們的應用特征而言,頁面切換實際上是僅僅內容數據的變化,比如切換一篇文檔,其 html 容器及樣式都是同一套,而差異僅僅只是在數據上,重新載入 html 及初始化 js 這部分耗時完全可以避免掉。讓 webview 組件及其容器內的 html 頁面常駐,在文檔切換的過程,僅僅對數據進行替換,這即是容器化方案。容器化方案省去了 webview 重復啟動和渲染 html 的問題,打開文檔,耗時只在做數據替換,真正做到了秒開。
容器切換
web 側如何感知到不同的頁面在進行互切換,數據如何做到替換呢?
首先在 app 打開的時候,文檔列表會進行數據預拉取,同時觸發客戶端預啟動容器,除此外,其他任意場景也能按需觸發容器啟動(后面會聊到)。容器內會提前進行 html 渲染和 js 執行,此時的數據是空的。用戶點擊文檔,客戶端會對打開 url 這一行為進行監聽,同時解析 url,取出唯一標識符, 判斷本地是否已經存在并且符合要求的數據,如果條件命中,直接使用已經打開的容器切出,通知到容器內的 web,web 收到通知,通過 url 取出標識符,從本地進行數據獲取,然后對數據進行替換渲染,從而完成頁面切換。
容器化數據替換
直接容器替換的思路省去了代碼加載和解析時間,但對于前端代碼而言,需要支持直接替換數據。大部分前端項目代碼設計都是 自執行調用
方式,支持容器化的前提是:需要對代碼改造成可支持 數據組裝和銷毀
。
// 大部分應用加載頁面初始化的場景 class App { public init() { initA(); initB(); // 初始化各種模塊 ... } } const app = new App(); app.init();
自執行調用后,大量的內部依賴模塊也依次進行初始化,然后數據常駐在內存中,通常對于加載一個正常網頁而言,用戶每次都是新開頁面,加載 html, 重新進行解析和初始化,并不會帶來什么問題。但是按照容器化思路,頁面不會重新載入,只進行數據替代,對于大型應用而言意味著成千上萬的模塊需要支持內存釋放和數據切換,一旦沒有處理好,會面臨嚴重的內存泄露和代碼健壯性問題。如何組織和管理這些代碼,支持可插拔式,讓整個頁面初始化流程都能鏈式組裝,可以進行配置,是進行容器化代碼改造的難點。
依賴倒置
依賴倒置原則的是指內部模塊不應該依賴外部模塊,底層模塊不應該依賴上層模塊。
哪些才是底層模塊,哪些才是上層模塊呢?通常而言,越穩定不變邏輯,應該是越底層,越接近用戶交互,容易變化的部分是上層。具體層級劃分需要分析應用的結構和依賴關系,良好劃分層級的應用是容器化改造的前提。
職責鏈模式
職責鏈模式是指每個對象都有接受請求的可能,這些對象連接成一條鏈,請求沿著這條鏈的傳遞,直到有對象處理,這樣做的好處是減少接受者和發送者直接的耦合。比如在一個頁面加載生命周期中,我們可以讓內部模塊到外部模塊都實現相應的生命周期職責,應用啟動和銷毀的過程,請求沿著指定鏈條從外到內傳遞,也可以按需指定跳躍某個模塊,這樣大大降低了模塊之間的耦合,從而更好的管理代碼。
export default interface IRestart{ emitter: EventEmitter; startDestroy(): void; destroy(): void; restart(): void; restartEnd(): void; // ... }
class Page { next: PageFlow|null; cache: { start: (() => Promise<any>)[]; end: (() => Promise<any>)[]; }; waitStart(callback: () => Promise<any>) { this.cache.start.push(callback); }; waitEnd(callback: () => Promise<any>) { this.cache.end.push(callback); }; setNext(flow: PageFlow) { this.next = flow; return flow; } // ... }
依賴注入
所謂依賴注入是當指 A 對象依賴另一個 B 對象時,不直接在 A 對象內初始化 B,而是通過外部環境進行初始化,作為參數傳入 A 對象中。這樣做的好處是當 B 模塊的初始化等條件發生變化時,不必在 A 對象中進行重復的修改。管理成百上千個這樣模塊相互依賴的代碼中,統一的依賴注入管理器會讓依賴關系管理變得更方便。
// 模塊加載器 class ServiceLoader { source: CONFIG; loaded: boolean; // 是否已加載 initialized: boolean; // 是否已初始 module: any; constructor(source: CONFIG) { this.loaded = false; this.initialized = false; // ... } async load(params?: any): Promise<any> { // ..load module return this.module; } //... }
// 模塊管理器 class ServiceCollection { stack: ServiceLoader[]; private services = new Map<CONFIG, ServiceLoader>(); constructor() { this.stack = []; } createLoader(config: CONFIG): ServiceLoader { const loader: ServiceLoader = new ServiceLoader(config); this.services.set(config, loader); return loader; } // ... }
initA () { const ALoader= this.serviceCollection.createLoader(CONFIG.A); const discussMobile = ALoader.init(this.app); }
數據預拉服務
容器是否會命中依賴兩個條件,其一對應離線包代碼是否下載好;其二對應版本的數據是否已經預拉緩存完畢。
用戶進入文檔管理首頁,首先會去拉取列表索引數據,然后通過列表數據 id 進行文檔內容數據做預拉,儲存在本地數據庫,本地數據庫的存儲可以參考前端離線化探索。
webview service
在整個數據預拉的過程,我們是通過一套獨立的客戶端后臺 webview 服務執行具體任務,獨立服務的好處是讓各種容器化基礎服務和文檔管理列表本身進行解耦,同時將拉取、解析、儲存數據這一對性能有消耗的過程放后臺服務,減少了列表用戶交互界面層的性能壓力。
另一方面,作為多端公用的一個服務,構建流程上單獨部署,更新代碼的時候能夠不依賴其他頁面,變得更靈活。
數據快照
對于純 dom 結構的文檔型品類,我們會在打開文檔,解析數據后,把生成的 html 緩存在本地數據庫一張快照表里。下一次切換容器時,在取本地數據去解析的同時,會判斷對應 id 在快照表是否存在緩存,如果有,直接取出來,覆蓋在 html 上,用戶可以提前看到上一次渲染的數據,等本地數據真正解析完,再展示可交互界面。解析數據準備渲染也是需要一個上百毫秒的過程,這一策略可以讓用戶提前看到內容。
預創建
有了極致的打開速度,如何優化新建速度呢。正常的新建流程是這樣的,用戶點擊新建按鈕,前端請求創建 cgi, 等待后臺創建成功返回新文檔 url,前端再新開 webview,加載展示頁面。我們可以看,由于需要等待創建接口返回的原因,到新建的過程比正常打開一個文檔還要更久。
怎么樣才能讓新建也做到秒開呢?思路和數據預拉取一樣,在用戶進入文檔首頁的同時,我們會提前預請求一批創建 id,然后緩存到本地,同時根據創建 id 生成一篇空白文檔數據,儲存在本地,標示狀態為未使用。用戶點擊新建按鈕,本質上是從本地取一個未使用的文檔 url,直接用容器切換打開,然后再和后臺進行同步。
秒開效果
容器化方案前:
容器化方案后:
監控與開關系統
容器方案使用了數據預取場景,命中率的監控非常重要。由于切換頁面的過程,如果命中容器,我們會接受來自客戶端的通知,在這個時機,我們可以進行上報。
另外一個非常重要的是容器能力的開關系統,在發布之初保持現網穩定性是非常重要的措施,但任何程序都不能保證沒有 bug,我們通過內部七彩石配置系統控制這套容器方案的各種特性在不同品類下是否啟用,同時這套配置也支持灰度和回滾方案,能夠應急各種突發問題。
灰度期容器間命中率
待優化的問題
容器化方案用各種預創建 webview 的方式換取了打開速度,app 內存占用上會比未使用容器化方案要大非常多,webview 的釋放時機、預加載數據的策略優化,及從客戶端到 web 端,如何更好的做內存管理是接下來需要進一步優化的點。
以上是“怎么解決HTML5頁面無縫閃開的問題”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。