這篇文章主要介紹怎么解決HTML5頁(yè)面無(wú)縫閃開(kāi)的問(wèn)題,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!
創(chuàng)新互聯(lián)公司長(zhǎng)期為上千客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對(duì)不同對(duì)象提供差異化的產(chǎn)品和服務(wù);打造開(kāi)放共贏平臺(tái),與合作伙伴共同營(yíng)造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為永和企業(yè)提供專業(yè)的網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作,永和網(wǎng)站改版等技術(shù)服務(wù)。擁有十余年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開(kāi)發(fā)。傳統(tǒng)方案的困境
無(wú)論是 html 離線,還是直出,以及讓 webview 啟動(dòng)和網(wǎng)絡(luò)請(qǐng)求并行 ,頁(yè)面的切換和打開(kāi)都無(wú)法避免 html 加載這一過(guò)程。對(duì)于大型應(yīng)用而言,龐大的 js 初始化解析和執(zhí)行會(huì)耗費(fèi)巨大的時(shí)間。
新的思考方向?
速度優(yōu)化的本質(zhì)是以空間換時(shí)間。我們是否可以從這個(gè)思路,將打開(kāi) webview 及解析 html 這以過(guò)程省略掉呢?答案是可以的。
容器化方案
容器化
即是我們最終探索與實(shí)踐的出來(lái)的一套方案。正常 web 頁(yè)面關(guān)閉后,webview 組件即會(huì)銷毀掉,下一次再打開(kāi)需要重新啟動(dòng)。通常讓 webview 保持常駐的做法可以節(jié)省 webview 啟動(dòng)時(shí)間, 但簡(jiǎn)單的常駐 webview 并不能做到頁(yè)面秒開(kāi),頁(yè)面打開(kāi)仍然需要重新解析 html。
對(duì)于我們的應(yīng)用特征而言,頁(yè)面切換實(shí)際上是僅僅內(nèi)容數(shù)據(jù)的變化,比如切換一篇文檔,其 html 容器及樣式都是同一套,而差異僅僅只是在數(shù)據(jù)上,重新載入 html 及初始化 js 這部分耗時(shí)完全可以避免掉。讓 webview 組件及其容器內(nèi)的 html 頁(yè)面常駐,在文檔切換的過(guò)程,僅僅對(duì)數(shù)據(jù)進(jìn)行替換,這即是容器化方案。容器化方案省去了 webview 重復(fù)啟動(dòng)和渲染 html 的問(wèn)題,打開(kāi)文檔,耗時(shí)只在做數(shù)據(jù)替換,真正做到了秒開(kāi)。
容器切換
web 側(cè)如何感知到不同的頁(yè)面在進(jìn)行互切換,數(shù)據(jù)如何做到替換呢?
首先在 app 打開(kāi)的時(shí)候,文檔列表會(huì)進(jìn)行數(shù)據(jù)預(yù)拉取,同時(shí)觸發(fā)客戶端預(yù)啟動(dòng)容器,除此外,其他任意場(chǎng)景也能按需觸發(fā)容器啟動(dòng)(后面會(huì)聊到)。容器內(nèi)會(huì)提前進(jìn)行 html 渲染和 js 執(zhí)行,此時(shí)的數(shù)據(jù)是空的。用戶點(diǎn)擊文檔,客戶端會(huì)對(duì)打開(kāi) url 這一行為進(jìn)行監(jiān)聽(tīng),同時(shí)解析 url,取出標(biāo)識(shí)符, 判斷本地是否已經(jīng)存在并且符合要求的數(shù)據(jù),如果條件命中,直接使用已經(jīng)打開(kāi)的容器切出,通知到容器內(nèi)的 web,web 收到通知,通過(guò) url 取出標(biāo)識(shí)符,從本地進(jìn)行數(shù)據(jù)獲取,然后對(duì)數(shù)據(jù)進(jìn)行替換渲染,從而完成頁(yè)面切換。
容器化數(shù)據(jù)替換
直接容器替換的思路省去了代碼加載和解析時(shí)間,但對(duì)于前端代碼而言,需要支持直接替換數(shù)據(jù)。大部分前端項(xiàng)目代碼設(shè)計(jì)都是自執(zhí)行調(diào)用
方式,支持容器化的前提是:需要對(duì)代碼改造成可支持數(shù)據(jù)組裝和銷毀
。
// 大部分應(yīng)用加載頁(yè)面初始化的場(chǎng)景 class App { public init() { initA(); initB(); // 初始化各種模塊 ... } } const app = new App(); app.init();
自執(zhí)行調(diào)用后,大量的內(nèi)部依賴模塊也依次進(jìn)行初始化,然后數(shù)據(jù)常駐在內(nèi)存中,通常對(duì)于加載一個(gè)正常網(wǎng)頁(yè)而言,用戶每次都是新開(kāi)頁(yè)面,加載 html, 重新進(jìn)行解析和初始化,并不會(huì)帶來(lái)什么問(wèn)題。但是按照容器化思路,頁(yè)面不會(huì)重新載入,只進(jìn)行數(shù)據(jù)替代,對(duì)于大型應(yīng)用而言意味著成千上萬(wàn)的模塊需要支持內(nèi)存釋放和數(shù)據(jù)切換,一旦沒(méi)有處理好,會(huì)面臨嚴(yán)重的內(nèi)存泄露和代碼健壯性問(wèn)題。如何組織和管理這些代碼,支持可插拔式,讓整個(gè)頁(yè)面初始化流程都能鏈?zhǔn)浇M裝,可以進(jìn)行配置,是進(jìn)行容器化代碼改造的難點(diǎn)。
依賴倒置
依賴倒置原則的是指內(nèi)部模塊不應(yīng)該依賴外部模塊,底層模塊不應(yīng)該依賴上層模塊。
哪些才是底層模塊,哪些才是上層模塊呢?通常而言,越穩(wěn)定不變邏輯,應(yīng)該是越底層,越接近用戶交互,容易變化的部分是上層。具體層級(jí)劃分需要分析應(yīng)用的結(jié)構(gòu)和依賴關(guān)系,良好劃分層級(jí)的應(yīng)用是容器化改造的前提。
職責(zé)鏈模式
職責(zé)鏈模式是指每個(gè)對(duì)象都有接受請(qǐng)求的可能,這些對(duì)象連接成一條鏈,請(qǐng)求沿著這條鏈的傳遞,直到有對(duì)象處理,這樣做的好處是減少接受者和發(fā)送者直接的耦合。比如在一個(gè)頁(yè)面加載生命周期中,我們可以讓內(nèi)部模塊到外部模塊都實(shí)現(xiàn)相應(yīng)的生命周期職責(zé),應(yīng)用啟動(dòng)和銷毀的過(guò)程,請(qǐng)求沿著指定鏈條從外到內(nèi)傳遞,也可以按需指定跳躍某個(gè)模塊,這樣大大降低了模塊之間的耦合,從而更好的管理代碼。
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; } // ... }
依賴注入
所謂依賴注入是當(dāng)指 A 對(duì)象依賴另一個(gè) B 對(duì)象時(shí),不直接在 A 對(duì)象內(nèi)初始化 B,而是通過(guò)外部環(huán)境進(jìn)行初始化,作為參數(shù)傳入 A 對(duì)象中。這樣做的好處是當(dāng) B 模塊的初始化等條件發(fā)生變化時(shí),不必在 A 對(duì)象中進(jìn)行重復(fù)的修改。管理成百上千個(gè)這樣模塊相互依賴的代碼中,統(tǒng)一的依賴注入管理器會(huì)讓依賴關(guān)系管理變得更方便。
// 模塊加載器 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); }
數(shù)據(jù)預(yù)拉服務(wù)
容器是否會(huì)命中依賴兩個(gè)條件,其一對(duì)應(yīng)離線包代碼是否下載好;其二對(duì)應(yīng)版本的數(shù)據(jù)是否已經(jīng)預(yù)拉緩存完畢。
用戶進(jìn)入文檔管理首頁(yè),首先會(huì)去拉取列表索引數(shù)據(jù),然后通過(guò)列表數(shù)據(jù) id 進(jìn)行文檔內(nèi)容數(shù)據(jù)做預(yù)拉,儲(chǔ)存在本地?cái)?shù)據(jù)庫(kù),本地?cái)?shù)據(jù)庫(kù)的存儲(chǔ)可以參考前端離線化探索。
webview service
在整個(gè)數(shù)據(jù)預(yù)拉的過(guò)程,我們是通過(guò)一套獨(dú)立的客戶端后臺(tái) webview 服務(wù)執(zhí)行具體任務(wù),獨(dú)立服務(wù)的好處是讓各種容器化基礎(chǔ)服務(wù)和文檔管理列表本身進(jìn)行解耦,同時(shí)將拉取、解析、儲(chǔ)存數(shù)據(jù)這一對(duì)性能有消耗的過(guò)程放后臺(tái)服務(wù),減少了列表用戶交互界面層的性能壓力。
另一方面,作為多端公用的一個(gè)服務(wù),構(gòu)建流程上單獨(dú)部署,更新代碼的時(shí)候能夠不依賴其他頁(yè)面,變得更靈活。
數(shù)據(jù)快照
對(duì)于純 dom 結(jié)構(gòu)的文檔型品類,我們會(huì)在打開(kāi)文檔,解析數(shù)據(jù)后,把生成的 html 緩存在本地?cái)?shù)據(jù)庫(kù)一張快照表里。下一次切換容器時(shí),在取本地?cái)?shù)據(jù)去解析的同時(shí),會(huì)判斷對(duì)應(yīng) id 在快照表是否存在緩存,如果有,直接取出來(lái),覆蓋在 html 上,用戶可以提前看到上一次渲染的數(shù)據(jù),等本地?cái)?shù)據(jù)真正解析完,再展示可交互界面。解析數(shù)據(jù)準(zhǔn)備渲染也是需要一個(gè)上百毫秒的過(guò)程,這一策略可以讓用戶提前看到內(nèi)容。
預(yù)創(chuàng)建
有了極致的打開(kāi)速度,如何優(yōu)化新建速度呢。正常的新建流程是這樣的,用戶點(diǎn)擊新建按鈕,前端請(qǐng)求創(chuàng)建 cgi, 等待后臺(tái)創(chuàng)建成功返回新文檔 url,前端再新開(kāi) webview,加載展示頁(yè)面。我們可以看,由于需要等待創(chuàng)建接口返回的原因,到新建的過(guò)程比正常打開(kāi)一個(gè)文檔還要更久。
怎么樣才能讓新建也做到秒開(kāi)呢?思路和數(shù)據(jù)預(yù)拉取一樣,在用戶進(jìn)入文檔首頁(yè)的同時(shí),我們會(huì)提前預(yù)請(qǐng)求一批創(chuàng)建 id,然后緩存到本地,同時(shí)根據(jù)創(chuàng)建 id 生成一篇空白文檔數(shù)據(jù),儲(chǔ)存在本地,標(biāo)示狀態(tài)為未使用。用戶點(diǎn)擊新建按鈕,本質(zhì)上是從本地取一個(gè)未使用的文檔 url,直接用容器切換打開(kāi),然后再和后臺(tái)進(jìn)行同步。
秒開(kāi)效果
容器化方案前:
容器化方案后:
監(jiān)控與開(kāi)關(guān)系統(tǒng)
容器方案使用了數(shù)據(jù)預(yù)取場(chǎng)景,命中率的監(jiān)控非常重要。由于切換頁(yè)面的過(guò)程,如果命中容器,我們會(huì)接受來(lái)自客戶端的通知,在這個(gè)時(shí)機(jī),我們可以進(jìn)行上報(bào)。
另外一個(gè)非常重要的是容器能力的開(kāi)關(guān)系統(tǒng),在發(fā)布之初保持現(xiàn)網(wǎng)穩(wěn)定性是非常重要的措施,但任何程序都不能保證沒(méi)有 bug,我們通過(guò)內(nèi)部七彩石配置系統(tǒng)控制這套容器方案的各種特性在不同品類下是否啟用,同時(shí)這套配置也支持灰度和回滾方案,能夠應(yīng)急各種突發(fā)問(wèn)題。
灰度期容器間命中率
待優(yōu)化的問(wèn)題
容器化方案用各種預(yù)創(chuàng)建 webview 的方式換取了打開(kāi)速度,app 內(nèi)存占用上會(huì)比未使用容器化方案要大非常多,webview 的釋放時(shí)機(jī)、預(yù)加載數(shù)據(jù)的策略優(yōu)化,及從客戶端到 web 端,如何更好的做內(nèi)存管理是接下來(lái)需要進(jìn)一步優(yōu)化的點(diǎn)。
以上是“怎么解決HTML5頁(yè)面無(wú)縫閃開(kāi)的問(wèn)題”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!
網(wǎng)頁(yè)名稱:怎么解決HTML5頁(yè)面無(wú)縫閃開(kāi)的問(wèn)題-創(chuàng)新互聯(lián)
鏈接地址:http://muchs.cn/article34/ceccpe.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供虛擬主機(jī)、小程序開(kāi)發(fā)、網(wǎng)站內(nèi)鏈、外貿(mào)網(wǎng)站建設(shè)、網(wǎng)站排名、云服務(wù)器
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容