前端性能優(yōu)化

本文首發(fā)于kmac007.me

性能優(yōu)化

資源壓縮合并,減少HTTP請(qǐng)求

由于HTTP是無狀態(tài)協(xié)議,意味著每次HTTP請(qǐng)求都需要建立通信鏈路、進(jìn)行數(shù)據(jù)傳輸,而在服務(wù)器端,每個(gè)HTTP請(qǐng)求都需要啟動(dòng)獨(dú)立的線程處理。這些通信和服務(wù)的開銷是很昂貴的,減少HTTP請(qǐng)求的數(shù)目可有效提高訪問性能。以下方法可以對(duì)資源進(jìn)行壓縮合并,減少HTTP請(qǐng)求:

  1. 合并CSS,并壓縮
  2. 合并JavaScript,并壓縮
  3. 圖片壓縮合并,通過CSS的操作偏移量顯示不同的圖片。(CSS Sprite,即俗稱:雪碧圖)

異步加載

異步加載的方式

  1. 動(dòng)態(tài)腳本加載
    通過JS動(dòng)態(tài)的創(chuàng)建<script>標(biāo)簽來動(dòng)態(tài)加載js文件。
  2. defer
<script src="./a.js" defer></script>
  1. async
<script src="./a.js" async></script>

異步加載的區(qū)別

如果不設(shè)置defer或者async,那么瀏覽器在遇到<script>標(biāo)簽時(shí),文檔的解析會(huì)停止,不再構(gòu)建DOM,會(huì)導(dǎo)致頁面阻塞直到腳本加載完畢。這是非常不好的用戶體驗(yàn),因此我們一般把<script>標(biāo)簽放置在<body>標(biāo)簽的最尾部。而deferasync兩者同樣可以解決這個(gè)問題。下面是二者的區(qū)別:

  1. defer是在HTML解析完之后才會(huì)執(zhí)行,如果是多個(gè),按照加載的順序依次執(zhí)行。
  2. async是在加載完之后立即執(zhí)行,如果是多個(gè),執(zhí)行順序和加載順序無關(guān)。

瀏覽器緩存

緩存的分類

強(qiáng)緩存

Expires Expires:Sat, 13 May 2017  14:22:34 GMT
Cache-Control Cache-Control: max-age=3600

協(xié)商緩存

Last-Modified If-Modified-Since Last-Modified: Sat, 13 May 2017  14:22:34 GMT
Etag If-None-Match

對(duì)于緩存這個(gè)部分,打算再寫一篇文章來描述。

使用CDN

CDN的全稱是Content Delivery Network,即內(nèi)容分發(fā)網(wǎng)絡(luò)。其基本思路是盡可能避開互聯(lián)網(wǎng)上有可能影響數(shù)據(jù)傳輸速度和穩(wěn)定性的瓶頸和環(huán)節(jié),使內(nèi)容傳輸?shù)母臁⒏€(wěn)定。通過在網(wǎng)絡(luò)各處放置節(jié)點(diǎn)服務(wù)器所構(gòu)成的在現(xiàn)有的互聯(lián)網(wǎng)基礎(chǔ)之上的一層智能虛擬網(wǎng)絡(luò),CDN系統(tǒng)能夠?qū)崟r(shí)地根據(jù)網(wǎng)絡(luò)流量和各節(jié)點(diǎn)的連接、負(fù)載狀況以及到用戶的距離和響應(yīng)時(shí)間等綜合信息將用戶的請(qǐng)求重新導(dǎo)向離用戶最近的服務(wù)節(jié)點(diǎn)上。其目的是使用戶可就近取得所需內(nèi)容,解決 Internet網(wǎng)絡(luò)擁擠的狀況,提高用戶訪問網(wǎng)站的響應(yīng)速度。

簡(jiǎn)單來說,CDN將內(nèi)容緩存到分布各地的CDN的節(jié)點(diǎn)上,根據(jù)用戶的訪問IP,使用戶可就近取得所需要的內(nèi)容,提高網(wǎng)絡(luò)響應(yīng)速度。

預(yù)解析DNS

DNS Prefetch,即DNS的預(yù)獲取,是前端優(yōu)化的一部分。一般來說,在前端優(yōu)化中與DNS有關(guān)的有兩點(diǎn);

  1. 減少DNS的解析次數(shù)
  2. DNS的預(yù)解析,即DNS Prefetch

一次DNS解析一般要耗費(fèi)20-120毫秒,減少DNS解析事件和次數(shù)是個(gè)很好的優(yōu)化方式。默認(rèn)情況下瀏覽器會(huì)對(duì)頁面中和當(dāng)前域名不在同一域的域名進(jìn)行預(yù)解析,并且緩存結(jié)果,這就是隱式的DNS Prefetch。如果向?qū)撁嬷袥]有出現(xiàn)的域進(jìn)行預(yù)解析,那么就要使用顯式的DNS Prefetch。

淘寶首頁中的DNS Prefetch

enter image description here

使用方法:
DNS Prefetch應(yīng)該盡量放在網(wǎng)頁的前面,推薦放在<meta>后面,具體用法如下:

<meta http-equiv="x-dns-prefetch-control" content="on">
<link rel="dns-prefetch" >
<link rel="dns-prefetch" >
<link rel="dns-prefetch" >

需要注意的是,雖然DNS Prefetch能夠加快頁面的解析速度,但是不能濫用。

如果需要禁止隱式的DNS Prefetch,可以使用以下的標(biāo)簽:

<meta http-equiv="x-dns-prefetch-control" content="off">

事件節(jié)流

比如,寫一個(gè)滾動(dòng)加載組件,監(jiān)聽scroll事件,這時(shí)每次滾動(dòng)都會(huì)執(zhí)行多次回調(diào)函數(shù),這是相當(dāng)消耗性能的。因此,我們可以通過事件節(jié)流的方式,減少滾動(dòng)回調(diào)函數(shù)的觸發(fā),從而提升性能。
例如:

var loadMore = document.getElementById('#loadMore')
var timer

//計(jì)時(shí)器的回調(diào)函數(shù)
function callback() {
  //距離頂部的距離
  const top = loadMore.getBoundingClientRect().top
  //視口高度
  const windowHeight = document.documentElement.clientHeight
  if(top && top < windowHeight){
    //loading...
  }
}

//監(jiān)聽滾動(dòng)事件并對(duì)函數(shù)節(jié)流
window.addEventListener('scroll', ()=>{
  if(timer) {
    clearTimeout(timer)
  }
  timer = setTimeout(callback, 100)
})

如上代碼,我們通過定時(shí)器的方式進(jìn)行函數(shù)節(jié)流,有效減少了回調(diào)函數(shù)的執(zhí)行。

減少DOM操作

對(duì)DOM操作的代價(jià)是高昂的,這在web應(yīng)用中通常是一個(gè)性能瓶頸。

在《高性能JavaScript》中這么比喻:“把DOM看成一個(gè)島嶼,把JavaScript(ECMAScript)看成另一個(gè)島嶼,兩者之間以一座收費(fèi)橋連接”。所以每次訪問DOM都會(huì)教一個(gè)過橋費(fèi),而訪問的次數(shù)越多,交的費(fèi)用也就越多。所以一般建議盡量減少過橋次數(shù)。

查詢和修改DOM元素會(huì)造成頁面的RepaintReflow。那么我們先了解下什么是RepaintReflow

RepaintReflow

Repaint(重繪)就是在一個(gè)元素的外觀被改變,但沒有改變布局(寬高)的情況下發(fā)生,如改變visibility、outline、背景色等等。

Reflow(重排) 的成本比 Repaint 的成本高得多的多。DOM Tree 里的每個(gè)結(jié)點(diǎn)都會(huì)有 Reflow 方法,一個(gè)結(jié)點(diǎn)的 Reflow 很有可能導(dǎo)致子結(jié)點(diǎn),甚至父點(diǎn)以及同級(jí)結(jié)點(diǎn)的 Reflow。在一些高性能的電腦上也許還沒什么,但是如果 Reflow 發(fā)生在手機(jī)上,那么這個(gè)過程是非常痛苦和耗電的。

每次設(shè)置style屬性改變節(jié)點(diǎn)樣式,每設(shè)置一次都會(huì)導(dǎo)致一次reflow,所以最好通過設(shè)置class的方式; 有動(dòng)畫效果的元素,它的position屬性應(yīng)當(dāng)設(shè)為fixed或absolute,這樣不會(huì)影響其它元素的布局;如果功能需求上不能設(shè)置position為fixed或absolute,那么就權(quán)衡速度的平滑性。

總之,因?yàn)?Reflow 有時(shí)確實(shí)不可避免,所以只能盡可能限制Reflow的影響范圍。

緩存DOM查詢

// 未緩存 DOM 查詢
for(let i = 0; i < document.getElementsByTagName('p').length; i++) {
  //...
}

上面這種情況下,每次循環(huán)都要進(jìn)行DOM查詢,非常影響性能。
我們通過如下的方式緩存DOM,這樣,就不需要每次都進(jìn)行DOM查詢,達(dá)到了減少DOM查詢的目的。

// 緩存了 DOM 查詢
var pList = document.getElementsByTagName('p')
for(let i = 0; i < pList.length; i++) {
  //...
}

合并DOM插入

在循環(huán)插入DOM時(shí),我們可以將部分生成的DOM節(jié)點(diǎn)插入到一個(gè)片段中,最后統(tǒng)一將片段插入到HTML中。

var listNode = document.getElementById('list')

// 要插入10個(gè)li
var frag = document.createDocumentFragment()
var li
for(let i = 0; i < 10; i++) {
  li = document.createElement('li')
  li.innderHTML = "List item" + i
  frag.appendChild(li)
}

listNode.appendChild(frag)

懶加載

懶加載的原理是通過自定義屬性標(biāo)簽存放圖片原有的src屬性,當(dāng)img標(biāo)簽出現(xiàn)在瀏覽器窗口范圍內(nèi)再依次將原src屬性填充以達(dá)到懶加載的效果。這種方法減少了開始加載網(wǎng)頁時(shí)的請(qǐng)求,減少瀏覽器卡死的幾率,減少了流量的消耗,同時(shí)提高了用戶體驗(yàn)。

主要步驟:

  1. 判斷圖片是否可見(滾動(dòng)高度 + 窗口高度 > 圖片到頁面頂部高度 && 圖片到頁面頂部高度 + 圖片高度 > 滾動(dòng)高度)
  2. 如果圖片可見,將存放在data-src中原本的src屬性填充src屬性中。

以下為一個(gè)圖片懶加載的示例:

<script async src="http://jsfiddle.net/enpk4e92/embed/result,js,html,css/dark/"></script>

SSR服務(wù)端渲染

服務(wù)端渲染可以提高性能。React.js和Vue.js目前都支持服務(wù)端渲染。在此不做深究。

參考

  1. Web前端性能優(yōu)化
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • AJax 優(yōu)化 緩存 Ajax 請(qǐng)求盡量使用GET, 僅取決于cookie數(shù)量 Cookie 優(yōu)化 減少Cooki...
    KeKeMars閱讀 9,472評(píng)論 5 89
  • 由于前端頁面絕大部分資源都是從服務(wù)器讀取,而且受瀏覽器限制,分配的計(jì)算機(jī)資源相對(duì)較少。。頁面的加載速度和性能往往受...
    Chyun閱讀 334評(píng)論 0 0
  • 圍繞前端的性能多如牛毛,涉及到方方面面,以我我們將圍繞PC瀏覽器和移動(dòng)端瀏覽器的優(yōu)化策略進(jìn)行羅列注意,是羅列不是展...
    流動(dòng)碼文閱讀 743評(píng)論 0 0
  • 前言 對(duì)于前端的性能話題,從來都沒有斷絕過。因?yàn)檫@個(gè)東西沒有最好,只有更好。而且往往也是業(yè)務(wù)的繁雜程度去決定優(yōu)化程...
    Layzimo閱讀 28,853評(píng)論 2 51
  • 網(wǎng)站的劃分一般為二:前端和后臺(tái)。我們可以理解成后臺(tái)是用來實(shí)現(xiàn)網(wǎng)站的功能的,比如:實(shí)現(xiàn)用戶注冊(cè),用戶能夠?yàn)槲恼掳l(fā)表評(píng)...
    ConRon閱讀 1,041評(píng)論 0 0

友情鏈接更多精彩內(nèi)容