自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

關(guān)于網(wǎng)頁內(nèi)容加速黑科技的趣談

開發(fā)
數(shù)周前,在倫敦 Heathrow 機場等飛機的空閑中,我順便處理了一些工作上的事情。不經(jīng)意間發(fā)現(xiàn) Github 在性能方面的一些問題,頗為詫異。通過新 tab 打開的頁面,其加載速度竟然比直接點擊鏈接打開的頁面要快。

[[206259]]

數(shù)周前,在倫敦 Heathrow 機場等飛機的空閑中,我順便處理了一些工作上的事情。不經(jīng)意間發(fā)現(xiàn) Github 在性能方面的一些問題,頗為詫異。通過新 tab 打開的頁面,其加載速度竟然比直接點擊鏈接打開的頁面要快。

點擊鏈接的同時復制鏈接并在新的 tab 頁中打開??梢钥吹剑M管先點擊的是鏈接,但渲染更快的卻是新 tab 中打開的頁面。

有一說一

頁面加載的時候,瀏覽器會接收網(wǎng)絡(luò)數(shù)據(jù)流,并將其輸出(pipe)給 HTML 解析器,HTML 解析器再將數(shù)據(jù)輸出到文檔。這意味著,頁面是邊加載邊渲染的。對于一個 100k 的頁面來說,瀏覽器很可能在接收到 20k 數(shù)據(jù)的時候就開始渲染出一些可用內(nèi)容了。

這個偉大又古老的特性,常常被開發(fā)者們有意無意地忽略了。多數(shù)提高加載性能的建議都歸結(jié)于一點,即“展示你所拿到的東西” —— 別怕,千萬不要傻傻等待一切加載完成之后再去展示內(nèi)容。

GitHub 當然是關(guān)注性能的,所以他們使用服務(wù)端渲染。但在同一個 tab 下瀏覽頁面時,他們用 JavaScript 重新實現(xiàn)了導航(navigation)功能,類似下面這樣:

  1. // …一堆重新實現(xiàn)瀏覽器導航功能代碼… 
  2. const response = await fetch('page-data.inc'); 
  3. const html = await response.text(); 
  4. document.querySelector('.content').innerHTML = html; 
  5. // …加載更多重新實現(xiàn)導航功能的代碼…  

這違反了規(guī)則,因為在 page-data.inc 下載完成之前什么事情都沒干。而服務(wù)端渲染版完全不會這樣囤積內(nèi)容,其內(nèi)容是流式的,這樣就要快得多了。就 Github 的客戶端渲染來說,很多 JavaScript 代碼完全減慢了渲染過程。

這里我僅僅只是拿 Github 舉例子 —— 這種反模式在單頁應(yīng)用中比比皆是。

在頁面之內(nèi)切換內(nèi)容可能確實有些好處,特別是存在大量腳本的情況下,無需重新執(zhí)行全部腳本即可更新內(nèi)容。但我們能否在不放棄流的情況下完成這樣的工作呢?我曾經(jīng)常說 JavaScript 沒有辦法對流進行解析,但其實還是有的……

<iframe> 和 document.write 大法

iframe 早已躋身圈內(nèi)最臭黑科技之列。但下面這個辦法就使用了 iframe 和 document.write(),這樣我們就能將內(nèi)容以流的形式添加到頁面中了。示例如下:

  1. // 創(chuàng)建 iframe: 
  2. const iframe = document.createElement('iframe'); 
  3.  
  4. // 添加到 document 中 (記得隱藏起來): 
  5. iframe.style.display = 'none'
  6. document.body.appendChild(iframe); 
  7.  
  8. // 等待 iframe 加載: 
  9. iframe.onload = () => { 
  10.   // 忽略其他 onload 操作: 
  11.   iframe.onload = null
  12.   // 添加一個虛擬標簽: 
  13.   iframe.contentDocument.write('<streaming-element>'); 
  14.   // 引用該元素: 
  15.   const streamingElement = iframe.contentDocument.querySelector('streaming-element'); 
  16.   // 將該元素從 iframe 中取出,并添加到文檔中: 
  17.   document.body.appendChild(streamingElement); 
  18.   // 寫入一些內(nèi)容 —— 這里應(yīng)該是異步的: 
  19.   iframe.contentDocument.write('<p>Hello!</p>'); 
  20.   // 繼續(xù)寫入內(nèi)容,直到完成: 
  21.   iframe.contentDocument.write('</streaming-element>'); 
  22.   iframe.contentDocument.close(); 
  23. }; 
  24.  
  25. //  iframe 初始化 
  26. iframe.src = '' 

雖然 Hello! 是寫到 iframe 中的,但它卻出現(xiàn)在了父級的 document 中!這是因為解析器維護了一個 敞開元素棧(stack of open elements),新創(chuàng)建的元素會被壓入棧中。就算我們把 <streaming-element/> 元素移出到 iframe 外面也不影響,就是這么任性。

此外,這種技術(shù)處理起 HTML 來,要比 innerHTML 更接近標準的頁面加載解析器。尤其是腳本依然會被下載,并在父級文檔的上下文中執(zhí)行 —— 只是在 Firefox 中完全不會執(zhí)行,但我認為這是個 bug更新: 其實腳本根本不應(yīng)該執(zhí)行(感謝 Simon Pieters 指出這一點),但 Edge、Safari、Chrome 都這么干。

接下來我們只需要從服務(wù)端獲取 HTML 數(shù)據(jù)流,每當一個部分的數(shù)據(jù)到達的時候,就調(diào)用 iframe.contentDocument.write()。流式傳輸和 fetch() 搭配起來會更好,但為了支持 Safari,我們還是使用 XHR 來 hack 一下吧。

我已經(jīng)寫好了一個 demo,可以拿來和 Github 進行對比。下面是在 3G 網(wǎng)絡(luò)下的測試結(jié)果:

 

點擊這里查看原始測試數(shù)據(jù)。

使用 iframe 進行流式渲染,頁面加載速度提高了 1.5 s。頭像也提前半秒鐘加載完成 —— 流式渲染意味著瀏覽器可以更早發(fā)現(xiàn)它們,并與內(nèi)容一起并行下載。

上面的方法對 Github 來說還是有效的,因為它的服務(wù)器返回的是 HTML。如果你使用的是框架,由框架自己管理 DOM 的展示,那可能就麻煩一些了。這種情況下可以看看下面這個次優(yōu)選項:

換行符分隔的 JSON

許多網(wǎng)站使用 JSON 驅(qū)動動態(tài)內(nèi)容。何其不幸,JSON 并不是一種對流友好的格式。盡管也有流式 JSON 解析器,可用起來卻并不那么簡單。

所以與其傳輸下面這樣一大塊 JSON 數(shù)據(jù):

  1.   "Comments": [ 
  2.     {"author""Alex""body""…"}, 
  3.     {"author""Jake""body""…"
  4.   ] 
  5.  

還不如像下面這樣一行輸出一個 JSON 對象:

  1. {"author""Alex""body""…"
  2.  
  3. {"author""Jake""body""…" 

這種被稱為 “換行符分隔的 JSON” 是有標準的:ndjson。給上面的內(nèi)容寫一個解析器就要簡單多了。到了 2017 年,我們也許可以使用一系列組合變換流(composable transform streams)來描述(譯者注:本文寫作于 2016 年 12 月):

  1. // 在 2017 年的某個時候可能會是這樣: 
  2. const response = await fetch('comments.ndjson'); 
  3. const comments = response.body 
  4.   // 從字節(jié)到文本: 
  5.   .pipeThrough(new TextDecoder()) 
  6.   // 一直緩沖,直到遇到換行符: 
  7.   .pipeThrough(splitStream('\n')) 
  8.   // 將內(nèi)容塊解析為JSON: 
  9.   .pipeThrough(parseJSON()); 
  10.  
  11. for await (const comment of comments) { 
  12.   // 處理每條評論,并將其添加到頁面: 
  13.   // (不管你使用的是什么模板或虛擬 DOM) 
  14.   addCommentToPage(comment); 
  15.  

在上面的代碼中,splitStream 和 parseJSON 是 可復用變換流(reusable transform streams)。與此同時,為了實現(xiàn)***程度的兼容,我們可以使用 XHR 進行 hack。

我再次新建了一個對比的 demo,下面是 3G 網(wǎng)絡(luò)下的結(jié)果:

 

點擊這里查看原始測試數(shù)據(jù)

與常規(guī) JSON 相比,ND-JSON 提前 1.5s 將內(nèi)容渲染到頁面上,盡管速度不如 iframe 方法那么快。在創(chuàng)建元素之前,必須等待完整的 JSON 對象出現(xiàn)。如果你的 JSON 文件體量巨大,可能會陷入對流的企盼之中。

單頁應(yīng)用?別著急

如前所述,Github 使用了大量的代碼,然而卻帶來這樣的性能問題。在客戶端重新實現(xiàn)導航功能是困難的,如果你需要改變頁面中的大塊內(nèi)容,這么做有可能并不值得。

可以拿我們的嘗試與簡單瀏覽器導航進行對比:

 

點擊這里查看原始測試數(shù)據(jù)。

打開一個簡單的沒有使用 JavaScript 瀏覽器導航的服務(wù)端渲染頁面的速度差不多是一樣的。但除去評論列表,測試頁面實在太過簡單。如果在不同頁面之間存在有大量重復的復雜內(nèi)容(主要是指可怕的廣告腳本),結(jié)果可能因?qū)嶋H情況而有差異,但一定要記得進行測試!很可能你編寫了一大堆代碼,然而只能帶來少的可憐的提升,甚至還可能減慢速度。 

責任編輯:龐桂玉 來源: Linux中國
相關(guān)推薦

2017-10-20 08:06:46

微軟

2018-05-16 15:09:37

AMD機械硬盤

2020-04-29 13:30:38

腳本Chrome黑科技

2017-07-07 10:24:56

云計算

2018-07-23 06:38:40

AI芯片數(shù)據(jù)中心

2016-07-14 16:40:56

黑科技

2023-09-22 08:27:39

2015-01-22 10:17:05

微軟win10

2016-11-14 11:08:06

戴爾服務(wù)器

2018-11-07 09:56:26

2023-12-10 14:19:31

JupyterPython編碼

2016-10-11 16:43:04

小米5s超聲波指紋識別

2016-11-10 19:31:00

蘇寧雙11

2020-07-10 10:34:22

人工智能無人機物聯(lián)網(wǎng)

2014-08-12 13:38:15

2017-03-24 16:45:34

銳捷

2016-03-18 09:52:40

物聯(lián)網(wǎng)wifi技術(shù)

2021-02-17 10:55:32

XRVRAR

2023-10-13 18:21:38

FasterRetinaNet密集
點贊
收藏

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