偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Web 端如何低成本打造 Native 體驗(yàn)?

開(kāi)發(fā) 開(kāi)發(fā)工具
Web 應(yīng)用在實(shí)際體驗(yàn)上和 Native 應(yīng)用仍然存在非常明顯的差距,那么如何低成本地把一個(gè)現(xiàn)有的網(wǎng)站改造成類 Native 的體驗(yàn)?zāi)?本文分享一種讓網(wǎng)站低成本漸進(jìn)式實(shí)現(xiàn) Native 化體驗(yàn)的方式——同屏渲染。

 Web 應(yīng)用在實(shí)際體驗(yàn)上和 Native 應(yīng)用仍然存在非常明顯的差距,那么如何低成本地把一個(gè)現(xiàn)有的網(wǎng)站改造成類 Native 的體驗(yàn)?zāi)?本文分享一種讓網(wǎng)站低成本漸進(jìn)式實(shí)現(xiàn) Native 化體驗(yàn)的方式——同屏渲染。

Web 端體驗(yàn)

在有了 PWA(Progressive web apps) 之后,Web Application 也具備了添加到桌面和離線訪問(wèn)等能力,但是實(shí)際體驗(yàn)上卻總是和 Native 應(yīng)用存在非常明顯的差距。

我們可以看一下 Alibaba 的 M 站和 iOS 應(yīng)用的錄屏(左邊為 WEB,右邊為 iOS APP):

??

??

 

我們可以看到,對(duì)于 Web Applicaiton 來(lái)說(shuō),在頁(yè)面中來(lái)回跳轉(zhuǎn)時(shí)訪問(wèn)的總是割裂的,從上一個(gè)頁(yè)面到下一個(gè)頁(yè)面需要等 loading ,返回時(shí)很多內(nèi)存狀態(tài)又都不在了,導(dǎo)致無(wú)法正確定位回之前的列表位置(這一點(diǎn)其實(shí)和不同的瀏覽器以及列表本身的實(shí)現(xiàn)方式有關(guān),也有一些方案可以規(guī)避這個(gè)問(wèn)題,在這里只是其中一個(gè) case)。

這樣對(duì)于用戶的體驗(yàn)傷害非常明顯,他能明確感覺(jué)到自己在用的并非一個(gè) Application 而是一個(gè) Website,而且在進(jìn)行復(fù)雜的操作時(shí)整個(gè)鏈路也非常容易被中斷。

而其實(shí)這種體驗(yàn)差異的根源,在于 B/S(Browser/Server)和 C/S(Client/Server)的差異。ServiceWorker 雖然提供了一些方案(例如 App Shell)讓我們較低成本的增強(qiáng)原有的體驗(yàn),但仍然難以解決頁(yè)面之間的割裂問(wèn)題,很多相同的代碼在不同頁(yè)面間重復(fù)執(zhí)行,每一次訪問(wèn)內(nèi)存狀態(tài)就會(huì)丟失。

渲染性能

當(dāng)我們?cè)谡f(shuō)體驗(yàn)的時(shí)候會(huì)顯得有點(diǎn)主觀,性能相比之下就容易衡量的多,而頁(yè)面割裂帶來(lái)的最為直觀的體驗(yàn)差距其實(shí)就來(lái)自于渲染性能的差異。

在 Web 端一個(gè)典型的 CSR(Client Side Rendering)要經(jīng)過(guò)的流程大致如下:

??

??

 

這其中有很多不符合我們預(yù)期的地方:

  • HTML/JS 都等到點(diǎn)擊后才開(kāi)始加載(這點(diǎn)可以通過(guò)預(yù)加載的手段解決,其中 HTML 的預(yù)加載可以通過(guò) ServiceWorker 進(jìn)行)。
  • framework 等 JS 在不同頁(yè)面間總是重復(fù)執(zhí)行的。
  • 加載 API 的時(shí)機(jī)非常晚,而加載 API 一般是耗時(shí)很長(zhǎng)而且可以并行的部分,理論上加載時(shí)機(jī)越早越好。

所以理想中的渲染流程應(yīng)該是下圖這樣:

??

??

 

其實(shí)對(duì)于 Native 應(yīng)用也是如此,用戶點(diǎn)擊時(shí)基本就會(huì)開(kāi)始加載 API 并且執(zhí)行下個(gè)頁(yè)面的邏輯。其實(shí)一個(gè)優(yōu)化的比較好(做了 preload 等)的 SPA 也是類似的效果,我們提前加載好下個(gè)頁(yè)面的 vendor ,點(diǎn)擊時(shí)直接只執(zhí)行下個(gè)頁(yè)面的邏輯即可。

然而實(shí)際上對(duì)于一個(gè)較大的現(xiàn)存站點(diǎn)來(lái)說(shuō)(例如 m.alibaba.com ),把整個(gè)網(wǎng)站作為一個(gè) SPA 來(lái)維護(hù)是不太現(xiàn)實(shí)的,一方面不能適應(yīng)當(dāng)前多人協(xié)作的現(xiàn)狀,另外一方面穩(wěn)定性上也不能接受修改一個(gè)頁(yè)面整個(gè)網(wǎng)站都要發(fā)布的方案。

那么,如何低成本的把一個(gè)現(xiàn)有的網(wǎng)站改造成類 Native 的體驗(yàn)?zāi)?

同屏渲染

在有了上面的思考后,我們就在想,有沒(méi)有一個(gè)方案在不做改造的前提下,在用戶點(diǎn)擊后,立即開(kāi)始數(shù)據(jù)的并行加載,同時(shí)把下個(gè)頁(yè)面動(dòng)態(tài)的加載進(jìn)來(lái),選擇性的保留上個(gè)頁(yè)面的一些內(nèi)容(例如正在加載中的數(shù)據(jù), jsonp , framework 層的對(duì)象等)而隔絕其他部分的干擾。

于是針對(duì)我們的場(chǎng)景產(chǎn)出了一個(gè)同屏渲染的方案:LightHub,所謂同屏渲染,即渲染過(guò)程中頁(yè)面不需要被卸載,所有的渲染行為都在一個(gè)上下文中發(fā)生。

??

??

 

這里我們需要幾個(gè)東西:

  • 能直接附著到現(xiàn)有頁(yè)面上的沙箱,用于把頁(yè)面還原到初始狀態(tài)(同時(shí)允許保留部分共享的部分)
  • 過(guò)渡動(dòng)畫(huà)
  • API 并行加載
  • 按照瀏覽器行為渲染 HTML
  • 按照瀏覽器行為觸發(fā)事件

??

??

 

沙箱

我們需要一個(gè)低成本把頁(yè)面還原會(huì)初始狀態(tài)、并且允許保留部分對(duì)象的沙箱機(jī)制,而且最好這個(gè)機(jī)制是可以直接低成本部署到現(xiàn)有頁(yè)面上的。其實(shí)這里的訴求和微前端碰到的問(wèn)題類似,我們受 qiankun 的沙箱機(jī)制啟發(fā),只需要在頁(yè)面的 中插入一小段內(nèi)聯(lián) JS 記錄:

  • window 上的全局變量
  • window/document 的 eventListener
  • 定時(shí)器:setInterval/setTimeout/requestAnimationFrame/requestIdleCallback
  • MutationObserver

在我們需要時(shí)我們只需要清空頁(yè)面的 DOM,還原變化的全局變量(這里和 qiankun 一樣采用的淺拷貝),eventListener,定時(shí)器和 MutationObserver,就能把頁(yè)面還原到初始狀態(tài)。

同時(shí),記錄的狀態(tài)也能封存到一個(gè)對(duì)象中,當(dāng)用戶從下個(gè)頁(yè)面 back 到上個(gè)頁(yè)面時(shí),我們可以直接把狀態(tài)還原到頁(yè)面上。

這里就需要在清空頁(yè)面狀態(tài)時(shí)選擇性的保留一些需要保留的對(duì)象:例如公共的 Framework,JSONP 請(qǐng)求的標(biāo)簽等。

過(guò)渡動(dòng)畫(huà)

這一點(diǎn)其實(shí)就沒(méi)有多復(fù)雜了,在頁(yè)面不需要被卸載和重新加載后,我們可以在用戶點(diǎn)擊后立即展示一個(gè)動(dòng)畫(huà)。目前采用的只是一個(gè)簡(jiǎn)單的從右側(cè) slide-in 的動(dòng)畫(huà)。

需要注意的是,由于在繪制動(dòng)畫(huà)的過(guò)程中我們往往正在執(zhí)行下個(gè)頁(yè)面的邏輯,我們需要注意使用 GPU 來(lái)繪制動(dòng)畫(huà),從而確保動(dòng)畫(huà)不會(huì)被 JS 執(zhí)行阻塞。這一點(diǎn)對(duì)于低端機(jī)尤為關(guān)鍵。

API 并行加載

其實(shí)在有了上面的沙箱機(jī)制后,API 的并行加載就不是難事了,需要注意的是我們需要保護(hù) API 并行加載本身的過(guò)程中產(chǎn)生的狀態(tài)(例如 setTimeout ),我們需要實(shí)現(xiàn)一個(gè) runInSharedContext 確保這其中的定時(shí)器不會(huì)在頁(yè)面切換時(shí)被卸載。

runInSharedContext(() => { 
// 這里的 setTimeout 不能是被記錄 & 清除的 setTimeout
setTimeout(() => window.sharedfetchDataPromise = fetch(res));
});

而在下個(gè)頁(yè)面消費(fèi)的只需要 window.sharedfetchDataPromise || fetch(url) 就能直接復(fù)用并行加載的 API 請(qǐng)求。

在我們的場(chǎng)景下為了讓這個(gè)問(wèn)題更加開(kāi)發(fā)者無(wú)感,封裝了一個(gè)叫做 redfox 的工具庫(kù),在同一個(gè)頁(yè)面環(huán)境執(zhí)行多次相同配置的請(qǐng)求會(huì)自動(dòng)復(fù)用,不需要開(kāi)發(fā)者手動(dòng)判斷。

按照瀏覽器行為渲染 HTML

這可能是其中最復(fù)雜的部分了,在我們抓到下個(gè)頁(yè)面的 HTML 后,不能只是簡(jiǎn)單的document.innerHTML = nextHTML ,這樣會(huì)導(dǎo)致和普通的瀏覽器行為完全不一致,樣式加載會(huì)導(dǎo)致閃屏,腳本的執(zhí)行順序不符合預(yù)期等等。

??

??

 

所以我們需要自己實(shí)現(xiàn)一個(gè) renderHTML ,將抓到的 HTML 解析后模擬瀏覽器的行為進(jìn)行渲染。

  • 播放動(dòng)畫(huà)
  • 先通過(guò)樣式隱藏 body
  • 異步將 CSS append 到頁(yè)面,等到 head 中的 CSS 加載完成并且動(dòng)畫(huà)播放完成后取消 body 的隱藏
  • 把 JS 按照類型和順序 append 到頁(yè)面
  • inline & 正常的阻塞后續(xù) DOM 和 js 的 append
  • defer 丟到 defer 隊(duì)列中
  • async 異步執(zhí)行,不阻塞后續(xù)
  • 按次序執(zhí)行 defer 隊(duì)列

這個(gè)部分的行為比較復(fù)雜,需要在較多的場(chǎng)景進(jìn)行測(cè)試,以及有相應(yīng)的單元測(cè)試保障邏輯的正確性。

??

??

 

按瀏覽器行為觸發(fā)事件

其實(shí)和上面渲染 HTML 相似,在渲染的過(guò)程中需要按照瀏覽器的行為觸發(fā)相應(yīng)的事件。

例如上個(gè)頁(yè)面卸載時(shí)依次觸發(fā) beforeunload => pagehide => unload ,在下個(gè)頁(yè)面加載時(shí)先把 readyState 重置,然后按照次序觸發(fā) domInteractive defer 的執(zhí)行和 DOMContentLoaded。

同樣的,單元測(cè)試在這個(gè)環(huán)節(jié)是必須的。

分析

Timeline 分析

從 Chrome 最后的 Timeline 看執(zhí)行邏輯基本是符合我們預(yù)期的,點(diǎn)擊后的瞬間 API 開(kāi)始加載并且基本上就開(kāi)始全力執(zhí)行下個(gè)頁(yè)面的渲染邏輯。

Framework 層的代碼基本也不需要再重復(fù)執(zhí)行。

??

??

 

內(nèi)存壓力

對(duì)于這種不卸載頁(yè)面的方案來(lái)說(shuō)最容易引起擔(dān)憂的可能就是內(nèi)存泄露問(wèn)題,其實(shí)按照上面的沙箱機(jī)制,只要我們確保 DOM、全局變量、定時(shí)器、時(shí)間監(jiān)聽(tīng)等能夠被正確清除,與之相關(guān)的閉包等就不會(huì)賴在內(nèi)存中不走。

從我們本地多次頻繁點(diǎn)擊切換頁(yè)面的反應(yīng)看,內(nèi)存隨著頁(yè)面的切換返回也會(huì)一次次回到初始狀態(tài),從理論上不存在直接導(dǎo)致內(nèi)存泄露的缺陷。

??

??

 

然而,由于我們?cè)试S在頁(yè)面間保留一部分的公共區(qū)域(上面稱之為 Service Layer),另外沙箱本身是一個(gè)約定沙箱而非安全沙箱(例如往 Element.prototype.xxx 屬性寫(xiě)東西就無(wú)法被攔截),對(duì)于一些不規(guī)范的寫(xiě)法仍然存在內(nèi)存泄露的風(fēng)險(xiǎn)。

這一點(diǎn)可能需要和 Native 端類似的內(nèi)存壓力監(jiān)控等方式來(lái)長(zhǎng)期觀察。

分階段打點(diǎn)

由于整個(gè) HTML 渲染過(guò)程都是我們自己實(shí)現(xiàn)的,所以整個(gè)渲染的各個(gè)階段可以自己打點(diǎn)記錄一些時(shí)間,下面就是一個(gè)例子:API 從 JS 請(qǐng)求到拿到耗時(shí) 124ms ,而實(shí)際上整個(gè)取數(shù)據(jù)(提前并行取的)花了 350ms 。每一個(gè) script 開(kāi)始執(zhí)行和執(zhí)行耗時(shí)也可以通過(guò)這種方式打上來(lái)。

??

??

 

這也可以為我們的頁(yè)面優(yōu)化提供一些指導(dǎo),例如 JS 的執(zhí)行時(shí)間是不是過(guò)晚,某段 JS 的執(zhí)行時(shí)間是不是過(guò)長(zhǎng)。

效果

最終的對(duì)比效果如下,左為同屏渲染,右為正常跳轉(zhuǎn),從線上的數(shù)據(jù)看性能提升大約從 2.8s => 1.8s 。

??

??

 

除了異步渲染的頁(yè)面外,我們針對(duì)一些原先是 SSR 的頁(yè)面也做了非常低成本的接入(不需要改造頁(yè)面,但是享受到的受益相對(duì)也更有限)。

??

??

 

但僅僅是上面這種跳轉(zhuǎn)體驗(yàn)和返回體驗(yàn)的改善,就讓我們的 Just For U 模塊的曝光屏數(shù)有穩(wěn)定 3% 的增長(zhǎng)。

總結(jié)

總結(jié)一下:

  • 類似 SPA 體驗(yàn)的客戶端渲染可以讓 Web 的體驗(yàn)更接近 Native。
  • 同屏渲染是一種讓網(wǎng)站低成本漸進(jìn)式實(shí)現(xiàn) Native 化體驗(yàn)的方式。
  • 更加沉浸的體驗(yàn)確實(shí)會(huì)讓用戶有意愿進(jìn)行更多地瀏覽。

局限

上面的方案仍然存在一些局限性,例如前面提到的需要開(kāi)發(fā)者防范內(nèi)存泄露的問(wèn)題,同時(shí)因?yàn)?History API 的限制,頁(yè)面必須是同域的,否則跳轉(zhuǎn)的 URL 無(wú)法滿足預(yù)期。

未來(lái)

關(guān)注 Chrome 動(dòng)態(tài)的同學(xué)也會(huì)了解到 Chrome 最近也退出了一個(gè)新的提案:Portal API,就是旨在解決我們上面提到的 Web 體驗(yàn)割裂的問(wèn)題。

能夠提供一個(gè)類似 iframe 的沙箱,以較低的成本實(shí)現(xiàn)頁(yè)面間的跳轉(zhuǎn)過(guò)渡等。在未來(lái) Protal 普及后(至少 Chrome 發(fā)布, Safari 跟進(jìn)后),我們就可以在新版本的瀏覽器中拋棄現(xiàn)在使用 JS 實(shí)現(xiàn)的沙箱機(jī)制,使用更加安全(且炫酷)的 Portal API 來(lái)實(shí)現(xiàn)同屏渲染。

在 Protal API 的支持下,我們也可以克服無(wú)法跨域的問(wèn)題,按照目前的草案,Portal 是支持跨域跳轉(zhuǎn)的。

責(zé)任編輯:武曉燕 來(lái)源: 51CTO專欄
相關(guān)推薦

2013-09-25 17:31:08

Storwize V5虛擬化存儲(chǔ)

2013-11-28 09:21:19

2020-06-18 09:40:13

智慧城市數(shù)據(jù)存儲(chǔ)基礎(chǔ)架構(gòu)

2023-02-21 10:32:56

ChatGPT人工智能

2011-08-06 23:03:01

京瓷復(fù)合一體機(jī)

2024-08-21 13:22:17

邊緣計(jì)算云平臺(tái)

2024-09-11 17:33:56

2009-03-19 10:00:06

2023-12-07 12:38:09

架構(gòu)低成本開(kāi)發(fā)

2010-06-22 16:04:53

2025-02-26 08:02:40

2011-04-11 14:56:04

2011-11-28 13:53:03

2021-12-21 23:21:16

DDOS防御安全

2024-10-25 10:00:00

云服務(wù)計(jì)算

2009-05-25 10:18:20

2009-07-06 14:03:01

高性能Web應(yīng)用緩存

2012-02-13 09:03:04

Web

2012-10-18 19:25:21

佳能

2022-06-30 08:55:15

DDoSWAF網(wǎng)絡(luò)攻擊
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)