成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

你必須懂的前端性能優(yōu)化

開(kāi)發(fā) 前端
對(duì)于 DNS 解析和 TCP 連接兩個(gè)步驟,我們前端可以做的努力非常有限。相比之下,HTTP 連接這一層面的優(yōu)化才是我們網(wǎng)絡(luò)優(yōu)化的核心。

從輸入U(xiǎn)RL加載起看方向

從輸入 URL 到頁(yè)面加載完成的過(guò)程:

   1. 首先做 DNS 查詢,如果這一步做了智能 DNS 解析的話,會(huì)提供訪問(wèn)速度最快的 IP 地址回來(lái)

   2. 接下來(lái)是 TCP 握手,應(yīng)用層會(huì)下發(fā)數(shù)據(jù)給傳輸層,這里 TCP 協(xié)議會(huì)指明兩端的端口號(hào),然后下發(fā)給網(wǎng)絡(luò)層。網(wǎng)絡(luò)層中的 IP 協(xié)議會(huì)確定 IP 地址,并且指示了數(shù)據(jù)傳輸中如何跳轉(zhuǎn)路由器。然后包會(huì)再被封裝到數(shù)據(jù)鏈路層的數(shù)據(jù)幀結(jié)構(gòu)中,最后就是物理層面的傳輸了

   3. TCP 握手結(jié)束后會(huì)進(jìn)行 TLS 握手,然后就開(kāi)始正式的傳輸數(shù)據(jù)

   4. 數(shù)據(jù)在進(jìn)入服務(wù)端之前,可能還會(huì)先經(jīng)過(guò)負(fù)責(zé)負(fù)載均衡的服務(wù)器,它的作用就是將請(qǐng)求合理的分發(fā)到多臺(tái)服務(wù)器上,這時(shí)假設(shè)服務(wù)端會(huì)響應(yīng)一個(gè) HTML 文件

   5. 首先瀏覽器會(huì)判斷狀態(tài)碼是什么,如果是 200 那就繼續(xù)解析,如果 400 或 500 的話就會(huì)報(bào)錯(cuò),如果 300 的話會(huì)進(jìn)行重定向,這里會(huì)有個(gè)重定向計(jì)數(shù)器,避免過(guò)多次的重定向,超過(guò)次數(shù)也會(huì)報(bào)錯(cuò)

   6. 瀏覽器開(kāi)始解析文件,如果是 gzip 格式的話會(huì)先解壓一下,然后通過(guò)文件的編碼格式知道該如何去解碼文件

   7. 文件解碼成功后會(huì)正式開(kāi)始渲染流程,先會(huì)根據(jù) HTML 構(gòu)建 DOM 樹,有 CSS 的話會(huì)去構(gòu)建 CSSOM 樹。如果遇到 script 標(biāo)簽的話,會(huì)判斷是否存在 async 或者 defer ,前者會(huì)并行進(jìn)行下載并執(zhí)行 JS,后者會(huì)先下載文件,然后等待 HTML 解析完成后順序執(zhí)行,如果以上都沒(méi)有,就會(huì)阻塞住渲染流程直到 JS 執(zhí)行完畢。遇到文件下載的會(huì)去下載文件,這里如果使用 HTTP 2.0 協(xié)議的話會(huì)極大的提高多圖的下載效率。

   8. 初始的 HTML 被完全加載和解析后會(huì)觸發(fā) DOMContentLoaded 事件

   9. CSSOM 樹和 DOM 樹構(gòu)建完成后會(huì)開(kāi)始生成 Render 樹,這一步就是確定頁(yè)面元素的布局、樣式等等諸多方面的東西

  10.  在生成 Render 樹的過(guò)程中,瀏覽器就開(kāi)始調(diào)用 GPU 繪制,合成圖層,將內(nèi)容顯示在屏幕上了

我們從輸入 URL 到顯示頁(yè)面這個(gè)過(guò)程中,涉及到網(wǎng)絡(luò)層面的,有三個(gè)主要過(guò)程:

  •  DNS 解析
  •  TCP 連接
  •  HTTP 請(qǐng)求/響應(yīng)

對(duì)于 DNS 解析和 TCP 連接兩個(gè)步驟,我們前端可以做的努力非常有限。相比之下,HTTP 連接這一層面的優(yōu)化才是我們網(wǎng)絡(luò)優(yōu)化的核心。

HTTP 優(yōu)化有兩個(gè)大的方向:

  •  減少請(qǐng)求次數(shù)
  •  減少單次請(qǐng)求所花費(fèi)的時(shí)間

瀏覽器緩存策略

瀏覽器緩存機(jī)制有四個(gè)方面,它們按照獲取資源時(shí)請(qǐng)求的優(yōu)先級(jí)依次排列如下:

  1.  Memory Cache
  2.  Service Worker Cache
  3.  HTTP Cache
  4.  Push Cache

MemoryCache

MemoryCache,是指存在內(nèi)存中的緩存。從優(yōu)先級(jí)上來(lái)說(shuō),它是瀏覽器最先嘗試去命中的一種緩存。從效率上來(lái)說(shuō),它是響應(yīng)速度最快的一種緩存。瀏覽器秉承的是“節(jié)約原則”,我們發(fā)現(xiàn),Base64 格式的圖片,幾乎永遠(yuǎn)可以被塞進(jìn) memory cache,這可以視作瀏覽器為節(jié)省渲染開(kāi)銷的“自保行為”;此外,體積不大的 JS、CSS 文件,也有較大地被寫入內(nèi)存的幾率——相比之下,較大的 JS、CSS 文件就沒(méi)有這個(gè)待遇了,內(nèi)存資源是有限的,它們往往被直接甩進(jìn)磁盤。

Service Worker Cache

Service Worker 是一種獨(dú)立于主線程之外的 Javascript 線程。它脫離于瀏覽器窗體,因此無(wú)法直接訪問(wèn) DOM。這樣獨(dú)立的個(gè)性使得 Service Worker 的“個(gè)人行為”無(wú)法干擾頁(yè)面的性能,這個(gè)“幕后工作者”可以幫我們實(shí)現(xiàn)離線緩存、消息推送和網(wǎng)絡(luò)代理等功能。我們借助 Service worker 實(shí)現(xiàn)的離線緩存就稱為 Service Worker Cache。

HTTP Cache

它又分為強(qiáng)緩存和協(xié)商緩存。優(yōu)先級(jí)較高的是強(qiáng)緩存,在命中強(qiáng)緩存失敗的情況下,才會(huì)走協(xié)商緩存。

對(duì)一條http get 報(bào)文的基本緩存處理過(guò)程包括7個(gè)步驟:

  1.  接收
  2.  解析
  3.  查詢,緩存查看是否有本地副本可用,如果沒(méi)有,就獲取一份副本
  4.  新鮮度檢測(cè), 緩存查看已緩存副本是否足夠新鮮,如果不是,就詢問(wèn)服務(wù)器是否有任何更新。
  5.  創(chuàng)建響應(yīng),緩存會(huì)用新的首部和已緩存的主體來(lái)構(gòu)建一條響應(yīng)報(bào)文。
  6.  發(fā)送,緩存通過(guò)網(wǎng)絡(luò)將響應(yīng)發(fā)回給客服端。
  7.  日志

強(qiáng)緩存

強(qiáng)緩存是利用 http 頭中的 Expires 和 Cache-Control 兩個(gè)字段來(lái)控制的。強(qiáng)緩存中,當(dāng)請(qǐng)求再次發(fā)出時(shí),瀏覽器會(huì)根據(jù)其中的 expires 和 cache-control 判斷目標(biāo)資源是否“命中”強(qiáng)緩存,若命中則直接從緩存中獲取資源,不會(huì)再與服務(wù)端發(fā)生通信。

是否足夠新鮮時(shí)期:

通過(guò) Expires: XXXX XXX XXX GMT (絕對(duì)日期時(shí)間,http/1.0) 或者 Cache-Control:max-age=XXXX (相對(duì)日期時(shí)間,http/1.1)在文檔標(biāo)明過(guò)期日期。

Cache-Control 相對(duì)于 expires 更加準(zhǔn)確,它的優(yōu)先級(jí)也更高。當(dāng) Cache-Control 與 expires 同時(shí)出現(xiàn)時(shí),我們以 Cache-Control 為準(zhǔn)。

關(guān)鍵字理解

public 與 private 是針對(duì)資源是否能夠被代理服務(wù)緩存而存在的一組對(duì)立概念。如果我們?yōu)橘Y源設(shè)置了 public,那么它既可以被瀏覽器緩存,也可以被代理服務(wù)器緩存;如果我們?cè)O(shè)置了 private,則該資源只能被瀏覽器緩存。private 為默認(rèn)值。

no-store與no-cache,no-cache 繞開(kāi)了瀏覽器:我們?yōu)橘Y源設(shè)置了 no-cache 后,每一次發(fā)起請(qǐng)求都不會(huì)再去詢問(wèn)瀏覽器的緩存情況,而是直接向服務(wù)端去確認(rèn)該資源是否過(guò)期(即走我們下文即將講解的協(xié)商緩存的路線)。no-store 比較絕情,顧名思義就是不使用任何緩存策略。在 no-cache 的基礎(chǔ)上,它連服務(wù)端的緩存確認(rèn)也繞開(kāi)了,只允許你直接向服務(wù)端發(fā)送請(qǐng)求、并下載完整的響應(yīng)。

協(xié)商緩存

協(xié)商緩存依賴于服務(wù)端與瀏覽器之間的通信。協(xié)商緩存機(jī)制下,瀏覽器需要向服務(wù)器去詢問(wèn)緩存的相關(guān)信息,進(jìn)而判斷是重新發(fā)起請(qǐng)求、下載完整的響應(yīng),還是從本地獲取緩存的資源。如果服務(wù)端提示緩存資源未改動(dòng)(Not Modified),資源會(huì)被重定向到瀏覽器緩存,這種情況下網(wǎng)絡(luò)請(qǐng)求對(duì)應(yīng)的狀態(tài)碼是 304。

協(xié)商緩存的實(shí)現(xiàn):從 Last-Modified 到 Etag,詳細(xì)自己百度,這里不再詳細(xì)展開(kāi)。

HTTP 緩存決策

當(dāng)我們的資源內(nèi)容不可復(fù)用時(shí),直接為 Cache-Control 設(shè)置 no-store,拒絕一切形式的緩存;否則考慮是否每次都需要向服務(wù)器進(jìn)行緩存有效確認(rèn),如果需要,那么設(shè) Cache-Control 的值為 no-cache;否則考慮該資源是否可以被代理服務(wù)器緩存,根據(jù)其結(jié)果決定是設(shè)置為 private 還是 public;然后考慮該資源的過(guò)期時(shí)間,設(shè)置對(duì)應(yīng)的 max-age 和 s-maxage 值;最后,配置協(xié)商緩存需要用到的 Etag、Last-Modified 等參數(shù)。

Push Cache

Push Cache 是指 HTTP2 在 server push 階段存在的緩存。

  •  Push Cache 是緩存的最后一道防線。瀏覽器只有在 Memory Cache、HTTP Cache 和 Service Worker Cache 均未命中的情況下才會(huì)去詢問(wèn) Push Cache。
  •  Push Cache 是一種存在于會(huì)話階段的緩存,當(dāng) session 終止時(shí),緩存也隨之釋放。
  •  不同的頁(yè)面只要共享了同一個(gè) HTTP2 連接,那么它們就可以共享同一個(gè) Push Cache。

CDN了解一番

CDN 的核心點(diǎn)有兩個(gè),一個(gè)是緩存,一個(gè)是回源。

“緩存”就是說(shuō)我們把資源 copy 一份到 CDN 服務(wù)器上這個(gè)過(guò)程,“回源”就是說(shuō) CDN 發(fā)現(xiàn)自己沒(méi)有這個(gè)資源(一般是緩存的數(shù)據(jù)過(guò)期了),轉(zhuǎn)頭向根服務(wù)器(或者它的上層服務(wù)器)去要這個(gè)資源的過(guò)程。

CDN 往往被用來(lái)存放靜態(tài)資源。所謂“靜態(tài)資源”,就是像 JS、CSS、圖片等不需要業(yè)務(wù)服務(wù)器進(jìn)行計(jì)算即得的資源。而“動(dòng)態(tài)資源”,顧名思義是需要后端實(shí)時(shí)動(dòng)態(tài)生成的資源,較為常見(jiàn)的就是 JSP、ASP 或者依賴服務(wù)端渲染得到的 HTML 頁(yè)面。

那“非純靜態(tài)資源”呢?它是指需要服務(wù)器在頁(yè)面之外作額外計(jì)算的 HTML 頁(yè)面。具體來(lái)說(shuō),當(dāng)我打開(kāi)某一網(wǎng)站之前,該網(wǎng)站需要通過(guò)權(quán)限認(rèn)證等一系列手段確認(rèn)我的身份、進(jìn)而決定是否要把 HTML 頁(yè)面呈現(xiàn)給我。這種情況下 HTML 確實(shí)是靜態(tài)的,但它和業(yè)務(wù)服務(wù)器的操作耦合,我們把它丟到CDN 上顯然是不合適的。

另外,CDN的域名必須和主業(yè)務(wù)服務(wù)器的域名不一樣,要不,同一個(gè)域名下面的Cookie各處跑,浪費(fèi)了性能流量的開(kāi)銷,CDN域名放在不同的域名下,可以完美地避免了不必要的 Cookie 的出現(xiàn)!

圖片優(yōu)化

二進(jìn)制位數(shù)與色彩的關(guān)系

在計(jì)算機(jī)中,像素用二進(jìn)制數(shù)來(lái)表示。不同的圖片格式中像素與二進(jìn)制位數(shù)之間的對(duì)應(yīng)關(guān)系是不同的。一個(gè)像素對(duì)應(yīng)的二進(jìn)制位數(shù)越多,它可以表示的顏色種類就越多,成像效果也就越細(xì)膩,文件體積相應(yīng)也會(huì)越大。

一個(gè)二進(jìn)制位表示兩種顏色(0|1 對(duì)應(yīng)黑|白),如果一種圖片格式對(duì)應(yīng)的二進(jìn)制位數(shù)有 n 個(gè),那么它就可以呈現(xiàn) 2^n 種顏色。

計(jì)算圖片大小

對(duì)于一張 100 100 像素的圖片來(lái)說(shuō),圖像上有 10000 個(gè)像素點(diǎn),如果每個(gè)像素的值是 RGBA 存儲(chǔ)的話,那么也就是說(shuō)每個(gè)像素有 4 個(gè)通道,每個(gè)通道 1 個(gè)字節(jié)(8 位 = 1個(gè)字節(jié)),所以該圖片大小大概為 39KB(10000 1 * 4 / 1024)。

但是在實(shí)際項(xiàng)目中,一張圖片可能并不需要使用那么多顏色去顯示,我們可以通過(guò)減少每個(gè)像素的調(diào)色板來(lái)相應(yīng)縮小圖片的大小。

了解了如何計(jì)算圖片大小的知識(shí),那么對(duì)于如何優(yōu)化圖片,想必大家已經(jīng)有 2 個(gè)思路了:

  •  減少像素點(diǎn)
  •  減少每個(gè)像素點(diǎn)能夠顯示的顏色

圖片類型要點(diǎn)

JPEG/JPG 特點(diǎn):有損壓縮、體積小、加載快、不支持透明,JPG 最大的特點(diǎn)是有損壓縮。這種高效的壓縮算法使它成為了一種非常輕巧的圖片格式。另一方面,即使被稱為“有損”壓縮,JPG的壓縮方式仍然是一種高質(zhì)量的壓縮方式:當(dāng)我們把圖片體積壓縮至原有體積的 50% 以下時(shí),JPG 仍然可以保持住 60% 的品質(zhì)。但當(dāng)它處理矢量圖形和 Logo 等線條感較強(qiáng)、顏色對(duì)比強(qiáng)烈的圖像時(shí),人為壓縮導(dǎo)致的圖片模糊會(huì)相當(dāng)明顯。

PNG 特點(diǎn):無(wú)損壓縮、質(zhì)量高、體積大、支持透明,PNG(可移植網(wǎng)絡(luò)圖形格式)是一種無(wú)損壓縮的高保真的圖片格式。8 和 24,這里都是二進(jìn)制數(shù)的位數(shù)。按照我們前置知識(shí)里提到的對(duì)應(yīng)關(guān)系,8 位的 PNG 最多支持 256 種顏色,而 24 位的可以呈現(xiàn)約 1600 萬(wàn)種顏色。PNG 圖片具有比 JPG 更強(qiáng)的色彩表現(xiàn)力,對(duì)線條的處理更加細(xì)膩,對(duì)透明度有良好的支持。它彌補(bǔ)了上文我們提到的 JPG 的局限性,唯一的 BUG 就是體積太大。

SVG 特點(diǎn):文本文件、體積小、不失真、兼容性好,SVG(可縮放矢量圖形)是一種基于 XML 語(yǔ)法的圖像格式。它和本文提及的其它圖片種類有著本質(zhì)的不同:SVG 對(duì)圖像的處理不是基于像素點(diǎn),而是是基于對(duì)圖像的形狀描述。

Base64 特點(diǎn):文本文件、依賴編碼、小圖標(biāo)解決方案,Base64 并非一種圖片格式,而是一種編碼方式。Base64 和雪碧圖一樣,是作為小圖標(biāo)解決方案而存在的。

WebP 特點(diǎn):年輕的全能型選手,WebP 像 JPEG 一樣對(duì)細(xì)節(jié)豐富的圖片信手拈來(lái),像 PNG 一樣支持透明,像 GIF 一樣可以顯示動(dòng)態(tài)圖片——它集多種圖片文件格式的優(yōu)點(diǎn)于一身。但是畢竟年輕,兼容性存在一些問(wèn)題。

渲染優(yōu)化

客戶端渲染

在客戶端渲染模式下,服務(wù)端會(huì)把渲染需要的靜態(tài)文件發(fā)送給客戶端,客戶端加載過(guò)來(lái)之后,自己在瀏覽器里跑一遍 JS,根據(jù) JS 的運(yùn)行結(jié)果,生成相應(yīng)的 DOM。頁(yè)面上呈現(xiàn)的內(nèi)容,你在 html 源文件里里找不到——這正是它的特點(diǎn)。

服務(wù)端渲染

在服務(wù)端渲染的模式下,當(dāng)用戶第一次請(qǐng)求頁(yè)面時(shí),由服務(wù)器把需要的組件或頁(yè)面渲染成HTML字符串,然后把它返回給客戶端。頁(yè)面上呈現(xiàn)的內(nèi)容,我們?cè)?html 源文件里也能找到。服務(wù)端渲染解決了一個(gè)非常關(guān)鍵的性能問(wèn)題——首屏加載速度過(guò)慢,也解決了SEO搜索引擎的問(wèn)題。

瀏覽器渲染過(guò)程解析

瀏覽器的渲染機(jī)制一般分為以下幾個(gè)步驟:

  1.  處理 HTML 并構(gòu)建 DOM 樹。
  2.  處理 CSS 構(gòu)建 CSSOM 樹
  3.  將 DOM 與 CSSOM 合并成一個(gè)渲染樹。
  4.  根據(jù)渲染樹來(lái)布局,計(jì)算每個(gè)節(jié)點(diǎn)的位置。
  5.  調(diào)用 GPU 繪制,合成圖層,顯示在屏幕上。

在渲染DOM的時(shí)候,瀏覽器所做的工作實(shí)際上是:

  1.  獲取DOM后分割為多個(gè)圖層
  2.  對(duì)每個(gè)圖層的節(jié)點(diǎn)計(jì)算樣式結(jié)果(Recalculate style–樣式重計(jì)算)
  3.  為每個(gè)節(jié)點(diǎn)生成圖形和位置(Layout–回流和重布局)
  4.  將每個(gè)節(jié)點(diǎn)繪制填充到圖層位圖中(Paint Setup和Paint–重繪)
  5.  圖層作為紋理上傳至GPU
  6.  復(fù)合多個(gè)圖層到頁(yè)面上生成最終屏幕圖像(Composite Layers–圖層重組)

基于渲染流程的 CSS 優(yōu)化建議

CSS 選擇符是從右到左進(jìn)行匹配的,比如 #myList li {}實(shí)際開(kāi)銷相當(dāng)高。

  •  避免使用通配符,只對(duì)需要用到的元素進(jìn)行選擇。
  •  關(guān)注可以通過(guò)繼承實(shí)現(xiàn)的屬性,避免重復(fù)匹配重復(fù)定義。
  •  少用標(biāo)簽選擇器。如果可以,用類選擇器替代。 錯(cuò)誤:#dataList li{} 正確:.dataList{}
  •  不要畫蛇添足,id 和 class 選擇器不應(yīng)該被多余的標(biāo)簽選擇器拖后腿。錯(cuò)誤:.dataList#title 正確: #title
  •  減少嵌套。后代選擇器的開(kāi)銷是最高的,因此我們應(yīng)該盡量將選擇器的深度降到最低(最高不要超過(guò)三層),盡可能使用類來(lái)關(guān)聯(lián)每一個(gè)標(biāo)簽元素。

CSS 的阻塞

CSS 是阻塞的資源。瀏覽器在構(gòu)建 CSSOM 的過(guò)程中,不會(huì)渲染任何已處理的內(nèi)容。即便 DOM 已經(jīng)解析完畢了,只要 CSSOM 不 OK,那么渲染這個(gè)事情就不 OK。我們將 CSS 放在 head 標(biāo)簽里 和盡快 啟用 CDN 實(shí)現(xiàn)靜態(tài)資源加載速度的優(yōu)化。

JS 的阻塞

JS 引擎是獨(dú)立于渲染引擎存在的。我們的 JS 代碼在文檔的何處插入,就在何處執(zhí)行。當(dāng) HTML 解析器遇到一個(gè) script 標(biāo)簽時(shí),它會(huì)暫停渲染過(guò)程,將控制權(quán)交給 JS 引擎。JS 引擎對(duì)內(nèi)聯(lián)的 JS 代碼會(huì)直接執(zhí)行,對(duì)外部 JS 文件還要先獲取到腳本、再進(jìn)行執(zhí)行。等 JS 引擎運(yùn)行完畢,瀏覽器又會(huì)把控制權(quán)還給渲染引擎,繼續(xù) CSSOM 和 DOM 的構(gòu)建。

DOM渲染優(yōu)化

先了解回流和重繪

  •  回流:當(dāng)我們對(duì) DOM 的修改引發(fā)了 DOM 幾何尺寸的變化(比如修改元素的寬、高或隱藏元素等)時(shí),瀏覽器需要重新計(jì)算元素的幾何屬性(其他元素的幾何屬性和位置也會(huì)因此受到影響),然后再將計(jì)算的結(jié)果繪制出來(lái)。這個(gè)過(guò)程就是回流(也叫重排)。
  •  重繪:當(dāng)我們對(duì) DOM 的修改導(dǎo)致了樣式的變化、卻并未影響其幾何屬性(比如修改了顏色或背景色)時(shí),瀏覽器不需重新計(jì)算元素的幾何屬性、直接為該元素繪制新的樣式(跳過(guò)了上圖所示的回流環(huán)節(jié))。這個(gè)過(guò)程叫做重繪。

重繪不一定導(dǎo)致回流,回流一定會(huì)導(dǎo)致重繪?;亓鞅戎乩L做的事情更多,帶來(lái)的開(kāi)銷也更大。在開(kāi)發(fā)中,要從代碼層面出發(fā),盡可能把回流和重繪的次數(shù)最小化。

例子剖析 

  1. <!DOCTYPE html>  
  2. <html lang="en">  
  3. <head>  
  4.   <meta charset="UTF-8">  
  5.   <meta name="viewport" content="width=device-width, initial-scale=1.0">  
  6.   <meta http-equiv="X-UA-Compatible" content="ie=edge">  
  7.   <title>DOM操作測(cè)試</title>  
  8. </head>  
  9. <body>  
  10.   <div id="container"></div>  
  11. </body>  
  12. </html>  
  13. for(var count=0;count<10000;count++){   
  14.   document.getElementById('container').innerHTML+='<span>我是一個(gè)小測(cè)試</span>'  //我們每一次循環(huán)都調(diào)用 DOM 接口重新獲取了一次 container 元素,額外開(kāi)銷  
  15. }  

進(jìn)化一: 

  1. // 只獲取一次container  
  2. let container = document.getElementById('container')  
  3. for(let count=0;count<10000;count++){   
  4.   container.innerHTML += '<span>我是一個(gè)小測(cè)試</span> 
  5. }  

進(jìn)化二: 

  1. //減少不必要的DOM更改  
  2. let container = document.getElementById('container')  
  3. let content = ''  
  4. for(let count=0;count<10000;count++){   
  5.   // 先對(duì)內(nèi)容進(jìn)行操作  
  6.   content += '<span>我是一個(gè)小測(cè)試</span> 
  7. }   
  8. // 內(nèi)容處理好了,最后再觸發(fā)DOM的更改  
  9. container.innerHTML = content 

事實(shí)上,考慮JS 的運(yùn)行速度,比 DOM 快得多這個(gè)特性。我們減少 DOM 操作的核心思路,就是讓 JS 去給 DOM 分壓。

在 DOM Fragment 中,DocumentFragment 接口表示一個(gè)沒(méi)有父級(jí)文件的最小文檔對(duì)象。它被當(dāng)做一個(gè)輕量版的 Document 使用,用于存儲(chǔ)已排好版的或尚未打理好格式的XML片段。因?yàn)?DocumentFragment 不是真實(shí) DOM 樹的一部分,它的變化不會(huì)引起 DOM 樹的重新渲染的操作(reflow),且不會(huì)導(dǎo)致性能等問(wèn)題。

進(jìn)化三: 

  1. let container = document.getElementById('container')  
  2. // 創(chuàng)建一個(gè)DOM Fragment對(duì)象作為容器  
  3. let content = document.createDocumentFragment()  
  4. for(let count=0;count<10000;count++){  
  5.   // span此時(shí)可以通過(guò)DOM API去創(chuàng)建  
  6.   let oSpan = document.createElement("span")  
  7.   oSpan.innerHTML = '我是一個(gè)小測(cè)試'  
  8.   // 像操作真實(shí)DOM一樣操作DOM Fragment對(duì)象  
  9.   content.appendChild(oSpan)  
  10.  
  11. // 內(nèi)容處理好了,最后再觸發(fā)真實(shí)DOM的更改  
  12. container.appendChild(content) 

進(jìn)化四:

當(dāng)涉及到過(guò)萬(wàn)調(diào)數(shù)據(jù)進(jìn)行渲染,而且要求不卡住畫面,如何解決?

如何在不卡住頁(yè)面的情況下渲染數(shù)據(jù),也就是說(shuō)不能一次性將幾萬(wàn)條都渲染出來(lái),而應(yīng)該一次渲染部分 DOM,那么就可以通過(guò) requestAnimationFrame 來(lái)每 16 ms 刷新一次。 

  1. <!DOCTYPE html>  
  2. <html lang="en">  
  3.   <head>  
  4.     <meta charset="UTF-8" />  
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0" />  
  6.     <meta http-equiv="X-UA-Compatible" content="ie=edge" />  
  7.     <title>Document</title>  
  8.   </head>  
  9.   <body>  
  10.     <ul>  
  11.       控件  
  12.     </ul>  
  13.     <script>  
  14.       setTimeout(() => {  
  15.         // 插入十萬(wàn)條數(shù)據(jù)  
  16.         const total = 100000  
  17.         // 一次插入 20 條,如果覺(jué)得性能不好就減少  
  18.         const once = 20  
  19.         // 渲染數(shù)據(jù)總共需要幾次  
  20.         const loopCount = total / once  
  21.         let countOfRender = 0  
  22.         let ul = document.querySelector('ul')  
  23.         function add() {  
  24.           // 優(yōu)化性能,插入不會(huì)造成回流  
  25.           const fragment = document.createDocumentFragment()  
  26.           for (let i = 0; i < once; i++) {  
  27.             const li = document.createElement('li')  
  28.             li.innerText = Math.floor(Math.random() * total)  
  29.             fragment.appendChild(li)  
  30.           }  
  31.           ul.appendChild(fragment)  
  32.           countOfRender += 1  
  33.           loop()  
  34.         }  
  35.         function loop() {  
  36.           if (countOfRender < loopCount) {  
  37.             window.requestAnimationFrame(add)  
  38.           }  
  39.         }  
  40.         loop()  
  41.       }, 0)  
  42.     </script>  
  43.   </body>  
  44. </html> 

window.requestAnimationFrame() 方法告訴瀏覽器您希望執(zhí)行動(dòng)畫并請(qǐng)求瀏覽器在下一次重繪之前調(diào)用指定的函數(shù)來(lái)更新動(dòng)畫。該方法使用一個(gè)回調(diào)函數(shù)作為參數(shù),這個(gè)回調(diào)函數(shù)會(huì)在瀏覽器重繪之前調(diào)用。

注意:若您想要在下次重繪時(shí)產(chǎn)生另一個(gè)動(dòng)畫畫面,您的回調(diào)例程必須調(diào)用 requestAnimationFrame()。

Event Loop

我們先了解javascript運(yùn)行機(jī)制,對(duì)渲染是大有幫助的,可以看我歷史文章JavaScript運(yùn)行機(jī)制,

Javascript運(yùn)行機(jī)制深入。

事件循環(huán)中的異步隊(duì)列有兩種:macro(宏任務(wù))隊(duì)列和 micro(微任務(wù))隊(duì)列。

常見(jiàn)的 macro-task 比如: setTimeout、setInterval、 setImmediate、script(整體代碼)、 I/O 操作、UI 渲染等。

常見(jiàn)的 micro-task 比如: process.nextTick、Promise、MutationObserver 等。

例子分析: 

  1. // task是一個(gè)用于修改DOM的回調(diào)  
  2. setTimeout(task, 0) 

上面代碼,現(xiàn)在 task 被推入的 macro 隊(duì)列。但因?yàn)?script 腳本本身是一個(gè) macro 任務(wù),所以本次執(zhí)行完 script 腳本之后,下一個(gè)步驟就要去處理 micro 隊(duì)列了,再往下就去執(zhí)行了一次 render,必須等待下一次的loop。 

  1. Promise.resolve().then(task) 

上面代碼,我們結(jié)束了對(duì) script 腳本的執(zhí)行,是不是緊接著就去處理 micro-task 隊(duì)列了?micro-task 處理完,DOM 修改好了,緊接著就可以走 render 流程了——不需要再消耗多余的一次渲染,不需要再等待一輪事件循環(huán),直接為用戶呈現(xiàn)最即時(shí)的更新結(jié)果。

當(dāng)我們需要在異步任務(wù)中實(shí)現(xiàn) DOM 修改時(shí),把它包裝成 micro 任務(wù)是相對(duì)明智的選擇。

上面說(shuō)了重繪與回流,Event loop,但很多人不知道的是,重繪和回流其實(shí)和 Event loop 有關(guān)。

  1.  當(dāng) Event loop 執(zhí)行完 Microtasks 后,會(huì)判斷 document 是否需要更新。因?yàn)闉g覽器是 60Hz 的刷新率,每 16ms 才會(huì)更新一次。
  2.  然后判斷是否有 resize 或者 scroll ,有的話會(huì)去觸發(fā)事件,所以 resize 和 scroll 事件也是至少 16ms 才會(huì)觸發(fā)一次,并且自帶節(jié)流功能。
  3.  判斷是否觸發(fā)了 media query
  4.  更新動(dòng)畫并且發(fā)送事件
  5.  判斷是否有全屏操作事件
  6.  執(zhí)行 requestAnimationFrame 回調(diào)
  7.  執(zhí)行 IntersectionObserver 回調(diào),該方法用于判斷元素是否可見(jiàn),可以用于懶加載上,但是兼容性不好
  8.  更新界面
  9.  以上就是一幀中可能會(huì)做的事情。如果在一幀中有空閑時(shí)間,就會(huì)去執(zhí)行 requestIdleCallback 回調(diào)。

節(jié)流與防抖

當(dāng)用戶進(jìn)行滾動(dòng),觸發(fā)scroll事件,用戶的每一次滾動(dòng)都將觸發(fā)我們的監(jiān)聽(tīng)函數(shù)。函數(shù)執(zhí)行是吃性能的,頻繁地響應(yīng)某個(gè)事件將造成大量不必要的頁(yè)面計(jì)算。因此,我們需要針對(duì)那些有可能被頻繁觸發(fā)的事件作進(jìn)一步地優(yōu)化。節(jié)流與防抖就很有必要了! 

責(zé)任編輯:龐桂玉 來(lái)源: segmentfault
相關(guān)推薦

2022-11-30 17:05:33

代碼程序場(chǎng)景

2017-09-28 12:03:40

前端

2019-05-16 15:35:36

2019-11-01 14:00:58

前端性能優(yōu)化代碼

2020-10-16 09:00:12

前端開(kāi)發(fā)技術(shù)

2022-05-17 09:02:30

前端性能優(yōu)化

2020-10-16 10:40:39

前端性能可視化

2022-11-16 12:03:13

性能優(yōu)化前端

2021-07-05 14:55:28

前端優(yōu)化圖片

2022-03-02 11:13:50

Web前端開(kāi)發(fā)

2022-09-13 12:56:28

前端優(yōu)化

2025-05-22 08:04:43

2019-06-20 17:39:12

Android啟動(dòng)優(yōu)化

2022-09-30 08:16:38

令牌客戶端隱藏式

2023-04-10 11:18:38

前端性能優(yōu)化

2021-05-08 08:35:33

Webpack前端性能

2012-01-10 16:22:25

Web

2021-02-02 13:45:31

Vue代碼前端

2015-05-07 15:13:22

JS實(shí)現(xiàn)JQueryJQuery

2013-01-22 15:27:23

WebWeb前端
點(diǎn)贊
收藏

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

主站蜘蛛池模板: 国产99在线 | 欧美 | 成人在线视频网站 | 日韩 国产 在线 | 国产伦精品 | 中文字幕免费中文 | 综合久久99| 亚洲日产精品 | 国产日韩欧美二区 | 久久天天躁狠狠躁夜夜躁2014 | 黄色一级网 | a网站在线观看 | 亚洲视频在线播放 | 在线观看涩涩视频 | 精品乱码一区二区三四区 | 国产黄色在线 | 香蕉一区二区 | 国产精品1区 | 成人在线免费观看视频 | 久久久久久国产精品 | 亚洲一区视频 | 亚洲伊人久久综合 | 久久久女女女女999久久 | 91福利网| 蜜月aⅴ国产精品 | 亚洲国产中文字幕 | 欧美狠狠操| 在线观看成人精品 | 福利av在线 | 欧美一级淫片免费视频黄 | 九九视频在线观看视频6 | 成人在线一区二区 | av片网站| 一区二区视频在线 | 欧美一级精品片在线看 | 在线视频国产一区 | 欧美a级成人淫片免费看 | 久久草视频 | 国产不卡在线播放 | 欧美日本高清 | 人操人人干人 | 欧美日本韩国一区二区三区 |