av激情亚洲男人的天堂国语,日韩欧美精品一中文字幕,无码av一区二区三区无码,国产又色又爽又刺激的a片,国产又色又爽又刺激的a片

前端性能優(yōu)化23條建議(2020)

性能優(yōu)化是把雙刃劍,有好的一面也有壞的一面。好的一面就是能提升網(wǎng)站性能,壞的一面就是配置麻煩,或者要遵守的規(guī)則太多。并且某些性能優(yōu)化規(guī)則并不適用所有場(chǎng)景,需要謹(jǐn)慎使用,請(qǐng)讀者帶著批判性的眼光來(lái)閱讀本文。

堅(jiān)守“ 做人真誠(chéng) · 做事靠譜 · 口碑至上 · 高效敬業(yè) ”的價(jià)值觀,專(zhuān)業(yè)網(wǎng)站建設(shè)服務(wù)10余年為成都成都柔性防護(hù)網(wǎng)小微創(chuàng)業(yè)公司專(zhuān)業(yè)提供成都企業(yè)網(wǎng)站定制營(yíng)銷(xiāo)網(wǎng)站建設(shè)商城網(wǎng)站建設(shè)手機(jī)網(wǎng)站建設(shè)小程序網(wǎng)站建設(shè)網(wǎng)站改版,從內(nèi)容策劃、視覺(jué)設(shè)計(jì)、底層架構(gòu)、網(wǎng)頁(yè)布局、功能開(kāi)發(fā)迭代于一體的高端網(wǎng)站建設(shè)服務(wù)。

1. 減少 HTTP 請(qǐng)求

一個(gè) HTTP 請(qǐng)求過(guò)程:

一個(gè) HTTP 請(qǐng)求需要經(jīng)歷以上過(guò)程,接下來(lái)看一個(gè)具體的例子:

這是一個(gè) HTTP 請(qǐng)求,請(qǐng)求的文件大小為 28.4KB。

名詞解釋?zhuān)?/p>

  •  Queueing: 在請(qǐng)求隊(duì)列中的時(shí)間。
  •  Stalled: 從TCP 連接建立完成,到真正可以傳輸數(shù)據(jù)之間的時(shí)間差,此時(shí)間包括代理協(xié)商時(shí)間。
  •  Proxy negotiation: 與代理服務(wù)器連接進(jìn)行協(xié)商所花費(fèi)的時(shí)間。
  •  DNS Lookup: 執(zhí)行DNS查找所花費(fèi)的時(shí)間,頁(yè)面上的每個(gè)不同的域都需要進(jìn)行DNS查找。
  •  Initial Connection / Connecting: 建立連接所花費(fèi)的時(shí)間,包括TCP握手/重試和協(xié)商SSL。
  •  SSL: 完成SSL握手所花費(fèi)的時(shí)間。
  •  Request sent: 發(fā)出網(wǎng)絡(luò)請(qǐng)求所花費(fèi)的時(shí)間,通常為一毫秒的時(shí)間。
  •  Waiting(TFFB): TFFB 是發(fā)出頁(yè)面請(qǐng)求到接收到應(yīng)答數(shù)據(jù)第一個(gè)字節(jié)的時(shí)間總和,它包含了 DNS 解析時(shí)間、 TCP 連接時(shí)間、發(fā)送 HTTP 請(qǐng)求時(shí)間和獲得響應(yīng)消息第一個(gè)字節(jié)的時(shí)間。
  •  Content Download: 接收響應(yīng)數(shù)據(jù)所花費(fèi)的時(shí)間。

從這個(gè)例子可以看出,真正下載數(shù)據(jù)的時(shí)間占比為 13.05 / 204.16 = 6.39%,文件越小,這個(gè)比例越小,文件越大,比例就越高。這就是為什么要建議將多個(gè)小文件合并為一個(gè)大文件,從而減少 HTTP 請(qǐng)求次數(shù)的原因。

參考資料:

  •  understanding-resource-timing

2. 使用 HTTP2

HTTP1.x 客戶(hù)端需要使用多個(gè)連接才能實(shí)現(xiàn)并發(fā)和縮短延遲;HTTP1.x 不會(huì)壓縮請(qǐng)求和響應(yīng)標(biāo)頭,從而導(dǎo)致不必要的網(wǎng)絡(luò)流量;HTTP1.x 不支持有效的資源優(yōu)先級(jí),致使底層 TCP 連接的利用率低下等等。

HTTP2 是對(duì)之前 HTTP 標(biāo)準(zhǔn)的擴(kuò)展,它通過(guò)支持標(biāo)頭字段壓縮和在同一連接上進(jìn)行多個(gè)并發(fā)交換,讓?xiě)?yīng)用更有效地利用網(wǎng)絡(luò)資源,減少感知的延遲時(shí)間。具體來(lái)說(shuō),它可以對(duì)同一連接上的請(qǐng)求和響應(yīng)消息進(jìn)行交錯(cuò)發(fā)送并為 HTTP 標(biāo)頭字段使用有效編碼。

HTTP2 還允許為請(qǐng)求設(shè)置優(yōu)先級(jí),讓更重要的請(qǐng)求更快速地完成,從而進(jìn)一步提升性能。

HTTP2 支持了多路復(fù)用,HTTP 連接變得十分廉價(jià),之前為了節(jié)省連接數(shù)所采用的類(lèi)似于「資源合并、資源內(nèi)聯(lián)」等優(yōu)化手段不再需要了。多路復(fù)用可以在一個(gè) TCP 連接上建立大量 HTTP 連接,也就不存在 HTTP 連接數(shù)限制了,HTTP1.x 中常見(jiàn)的「靜態(tài)域名」優(yōu)化策略不但用不上了,還會(huì)帶來(lái)負(fù)面影響,需要去掉。另外,HTTP2 的頭部壓縮功能也能大幅減少 HTTP 協(xié)議頭部帶來(lái)的開(kāi)銷(xiāo)。但是,要等HTTP1.x 完全退出舞臺(tái)還需要一段時(shí)間。

現(xiàn)在有很多網(wǎng)站已經(jīng)開(kāi)始使用 HTTP2 了,例如知乎:

其中 h2 是指 HTTP2 協(xié)議,http/1.1 則是指 HTTP1.1 協(xié)議。

參考資料:

  •  HTTP2 簡(jiǎn)介
  •  HTTP2 與 WEB 性能優(yōu)化(三)
  •  基于Node.js的HTTP/2 Server實(shí)踐

3. 使用服務(wù)端渲染

客戶(hù)端渲染: 獲取 HTML 文件,根據(jù)需要下載 JavaScript 文件,運(yùn)行文件,生成 DOM,再渲染。

服務(wù)端渲染:服務(wù)端返回 HTML 文件,客戶(hù)端只需解析 HTML。

  •  優(yōu)點(diǎn):首屏渲染快,SEO 好。
  •  缺點(diǎn):配置麻煩。

參考資料:

  •  Vue.js 服務(wù)器端渲染指南

4. 靜態(tài)資源使用 CDN

內(nèi)容分發(fā)網(wǎng)絡(luò)(CDN)是一組分布在多個(gè)不同地理位置的 Web 服務(wù)器。我們都知道,當(dāng)服務(wù)器離用戶(hù)越遠(yuǎn)時(shí),延遲越高。CDN 就是為了解決這一問(wèn)題,在多個(gè)位置部署服務(wù)器,讓用戶(hù)離服務(wù)器更近,從而縮短請(qǐng)求時(shí)間。

參考資料:

  •  CDN是什么?使用CDN有什么優(yōu)勢(shì)?

5. 將 CSS 放在文件頭部,JavaScript 文件放在底部

所有放在 head 標(biāo)簽里的 CSS 和 JS 文件都會(huì)堵塞渲染。如果這些 CSS 和 JS 需要加載和解析很久的話(huà),那么頁(yè)面就空白了。所以 JS 文件要放在底部,等 HTML 解析完了再加載 JS 文件。

那為什么 CSS 文件還要放在頭部呢?

因?yàn)橄燃虞d HTML 再加載 CSS,會(huì)讓用戶(hù)第一時(shí)間看到的頁(yè)面是沒(méi)有樣式的、“丑陋”的,為了避免這種情況發(fā)生,就要將 CSS 文件放在頭部了。

另外,JS 文件也不是不可以放在頭部,只要給 script 標(biāo)簽加上 defer 屬性就可以了,異步下載,延遲執(zhí)行。

6. 使用字體圖標(biāo) iconfont 代替圖片圖標(biāo)

字體圖標(biāo)就是將圖標(biāo)制作成一個(gè)字體,使用時(shí)就跟字體一樣,可以設(shè)置屬性,例如 font-size、color 等等,非常方便。并且字體圖標(biāo)是矢量圖,不會(huì)失真。還有一個(gè)優(yōu)點(diǎn)是生成的文件特別小。

  •  Iconfont-阿里巴巴矢量圖標(biāo)庫(kù)

7. 善用緩存,不重復(fù)加載相同的資源

為了避免用戶(hù)每次訪問(wèn)網(wǎng)站都得請(qǐng)求文件,我們可以通過(guò)添加 Expires 頭來(lái)控制這一行為。Expires 設(shè)置了一個(gè)時(shí)間,只要在這個(gè)時(shí)間之前,瀏覽器都不會(huì)請(qǐng)求文件,而是直接使用緩存。

不過(guò)這樣會(huì)產(chǎn)生一個(gè)問(wèn)題,當(dāng)文件更新了怎么辦?怎么通知瀏覽器重新請(qǐng)求文件?

可以通過(guò)更新頁(yè)面中引用的 CSS 鏈接地址,讓瀏覽器主動(dòng)放棄緩存,加載新資源。

具體做法是把 CSS 地址的修改與文件內(nèi)容關(guān)聯(lián)起來(lái),也就是說(shuō),只有文件內(nèi)容變化,才會(huì)導(dǎo)致相應(yīng) CSS 地址的變更,從而實(shí)現(xiàn)文件級(jí)別的精確緩存控制。什么東西與文件內(nèi)容相關(guān)呢?我們會(huì)很自然的聯(lián)想到利用數(shù)據(jù)摘要要算法對(duì)文件求摘要信息,摘要信息與文件內(nèi)容一一對(duì)應(yīng),就有了一種可以精確到單個(gè)文件粒度的緩存控制依據(jù)了。

參考資料:

  •  張?jiān)讫?-大公司里怎樣開(kāi)發(fā)和部署前端代碼?

8. 壓縮文件

壓縮文件可以減少文件下載時(shí)間,讓用戶(hù)體驗(yàn)性更好。

得益于 webpack 和 node 的發(fā)展,現(xiàn)在壓縮文件已經(jīng)非常方便了。

在 webpack 可以使用如下插件進(jìn)行壓縮:

  •  JavaScript:UglifyPlugin
  •  CSS :MiniCssExtractPlugin
  •  HTML:HtmlWebpackPlugin

其實(shí),我們還可以做得更好。那就是使用 gzip 壓縮??梢酝ㄟ^(guò)向 HTTP 請(qǐng)求頭中的 Accept-Encoding 頭添加 gzip 標(biāo)識(shí)來(lái)開(kāi)啟這一功能。當(dāng)然,服務(wù)器也得支持這一功能。

gzip 是目前最流行和最有效的壓縮方法。舉個(gè)例子,我用 Vue 開(kāi)發(fā)的項(xiàng)目構(gòu)建后生成的 app.js 文件大小為 1.4MB,使用 gzip 壓縮后只有 573KB,體積減少了將近 60%。

附上 webpack 和 node 配置 gzip 的使用方法。

下載插件

 
 
 
 
  1. npm install compression-webpack-plugin --save-dev  
  2. npm install compression 

webpack 配置

 
 
 
 
  1. const CompressionPlugin = require('compression-webpack-plugin');  
  2. module.exports = {  
  3.   plugins: [new CompressionPlugin()],  

node 配置

 
 
 
 
  1. const compression = require('compression')  
  2. // 在其他中間件前使用  
  3. app.use(compression()) 

9. 圖片優(yōu)化

(1). 圖片延遲加載

在頁(yè)面中,先不給圖片設(shè)置路徑,只有當(dāng)圖片出現(xiàn)在瀏覽器的可視區(qū)域時(shí),才去加載真正的圖片,這就是延遲加載。對(duì)于圖片很多的網(wǎng)站來(lái)說(shuō),一次性加載全部圖片,會(huì)對(duì)用戶(hù)體驗(yàn)造成很大的影響,所以需要使用圖片延遲加載。

首先可以將圖片這樣設(shè)置,在頁(yè)面不可見(jiàn)時(shí)圖片不會(huì)加載:

 
 
 
 
  1.  

等頁(yè)面可見(jiàn)時(shí),使用 JS 加載圖片:

 
 
 
 
  1. const img = document.querySelector('img')  
  2. imgimg.src = img.dataset.src 

這樣圖片就加載出來(lái)了,完整的代碼可以看一下參考資料。

參考資料:

  •  web 前端圖片懶加載實(shí)現(xiàn)原理

(2). 響應(yīng)式圖片

響應(yīng)式圖片的優(yōu)點(diǎn)是瀏覽器能夠根據(jù)屏幕大小自動(dòng)加載合適的圖片。

通過(guò) picture 實(shí)現(xiàn)

 
 
 
 
  1.   
  2.       
  3.       
  4.       
  5.  

通過(guò) @media 實(shí)現(xiàn)

 
 
 
 
  1. @media (min-width: 769px) {  
  2.     .bg {  
  3.         background-image: url(bg1080.jpg);  
  4.     }  
  5. }  
  6. @media (max-width: 768px) {  
  7.     .bg {  
  8.         background-image: url(bg768.jpg);  
  9.     }  

(3). 調(diào)整圖片大小

例如,你有一個(gè) 1920 * 1080 大小的圖片,用縮略圖的方式展示給用戶(hù),并且當(dāng)用戶(hù)鼠標(biāo)懸停在上面時(shí)才展示全圖。如果用戶(hù)從未真正將鼠標(biāo)懸停在縮略圖上,則浪費(fèi)了下載圖片的時(shí)間。

所以,我們可以用兩張圖片來(lái)實(shí)行優(yōu)化。一開(kāi)始,只加載縮略圖,當(dāng)用戶(hù)懸停在圖片上時(shí),才加載大圖。還有一種辦法,即對(duì)大圖進(jìn)行延遲加載,在所有元素都加載完成后手動(dòng)更改大圖的 src 進(jìn)行下載。

(4). 降低圖片質(zhì)量

例如 JPG 格式的圖片,100% 的質(zhì)量和 90% 質(zhì)量的通??床怀鰜?lái)區(qū)別,尤其是用來(lái)當(dāng)背景圖的時(shí)候。我經(jīng)常用 PS 切背景圖時(shí), 將圖片切成 JPG 格式,并且將它壓縮到 60% 的質(zhì)量,基本上看不出來(lái)區(qū)別。

除此之外,網(wǎng)上還有很多在線(xiàn)壓縮圖片的網(wǎng)站,大家可以自行搜索。

(5). 盡可能利用 CSS3 效果代替圖片

有很多圖片使用 CSS 效果(漸變、陰影等)就能畫(huà)出來(lái),這種情況選擇 CSS3 效果更好。因?yàn)榇a大小通常是圖片大小的幾分之一甚至幾十分之一。

10. 通過(guò) webpack 按需加載 JavaScript 代碼

懶加載或者按需加載,是一種很好的優(yōu)化網(wǎng)頁(yè)或應(yīng)用的方式。這種方式實(shí)際上是先把你的代碼在一些邏輯斷點(diǎn)處分離開(kāi),然后在一些代碼塊中完成某些操作后,立即引用或即將引用另外一些新的代碼塊。這樣加快了應(yīng)用的初始加載速度,減輕了它的總體體積,因?yàn)槟承┐a塊可能永遠(yuǎn)不會(huì)被加載。

如果你使用腳手架來(lái)構(gòu)建項(xiàng)目,一般配置起來(lái)非常簡(jiǎn)單,具體細(xì)節(jié)可看一下 webpack 文檔。

參考資料:

  •  懶加載
  •  Vue 路由懶加載

11. 減少重繪重排

瀏覽器渲染過(guò)程

  1.  解析HTML生成DOM樹(shù)。
  2.  解析CSS生成CSSOM規(guī)則樹(shù)。
  3.  將DOM樹(shù)與CSSOM規(guī)則樹(shù)合并在一起生成渲染樹(shù)。
  4.  遍歷渲染樹(shù)開(kāi)始布局,計(jì)算每個(gè)節(jié)點(diǎn)的位置大小信息。
  5.  將渲染樹(shù)每個(gè)節(jié)點(diǎn)繪制到屏幕。

重排

當(dāng)改變 DOM 元素位置或大小時(shí),會(huì)導(dǎo)致瀏覽器重新生成渲染樹(shù),這個(gè)過(guò)程叫重排。

重繪

當(dāng)重新生成渲染樹(shù)后,就要將渲染樹(shù)每個(gè)節(jié)點(diǎn)繪制到屏幕,這個(gè)過(guò)程叫重繪。不是所有的動(dòng)作都會(huì)導(dǎo)致重排,例如改變字體顏色,只會(huì)導(dǎo)致重繪。記住,重排會(huì)導(dǎo)致重繪,重繪不會(huì)導(dǎo)致重排 。

重排和重繪這兩個(gè)操作都是非常昂貴的,因?yàn)?JavaScript 引擎線(xiàn)程與 GUI 渲染線(xiàn)程是互斥,它們同時(shí)只能一個(gè)在工作。

什么操作會(huì)導(dǎo)致重排?

  •  添加或刪除可見(jiàn)的 DOM 元素
  •  元素位置改變
  •  元素尺寸改變
  •  內(nèi)容改變
  •  瀏覽器窗口尺寸改變

如何減少重排重繪?

  •  用 JavaScript 修改樣式時(shí),最好不要直接寫(xiě)樣式,而是替換 class 來(lái)改變樣式。
  •  如果要對(duì) DOM 元素執(zhí)行一系列操作,可以將 DOM 元素脫離文檔流,修改完成后,再將它帶回文檔。推薦使用隱藏元素(display:none)或文檔碎片(DocumentFragement),都能很好的實(shí)現(xiàn)這個(gè)方案。

12. 使用事件委托

事件委托利用了事件冒泡,只指定一個(gè)事件處理程序,就可以管理某一類(lèi)型的所有事件。所有用到按鈕的事件(多數(shù)鼠標(biāo)事件和鍵盤(pán)事件)都適合采用事件委托技術(shù), 使用事件委托可以節(jié)省內(nèi)存。

 
 
 
 
    •   
    •   
    • 蘋(píng)果
    •   
    •   
    • 香蕉
    •   
    •   
    • 鳳梨
    •   
      
  1. // good  
  2. document.querySelector('ul').onclick = (event) => {  
  3.   const target = event.target  
  4.   if (target.nodeName === 'LI') {  
  5.     console.log(target.innerHTML)  
  6.   }  
  7. }  
  8. // bad  
  9. document.querySelectorAll('li').forEach((e) => {  
  10.   e.onclick = function() {  
  11.     console.log(this.innerHTML)  
  12.   }  
  13. })  

13. 注意程序的局部性

一個(gè)編寫(xiě)良好的計(jì)算機(jī)程序常常具有良好的局部性,它們傾向于引用最近引用過(guò)的數(shù)據(jù)項(xiàng)附近的數(shù)據(jù)項(xiàng),或者最近引用過(guò)的數(shù)據(jù)項(xiàng)本身,這種傾向性,被稱(chēng)為局部性原理。有良好局部性的程序比局部性差的程序運(yùn)行得更快。

局部性通常有兩種不同的形式:

  •  時(shí)間局部性:在一個(gè)具有良好時(shí)間局部性的程序中,被引用過(guò)一次的內(nèi)存位置很可能在不遠(yuǎn)的將來(lái)被多次引用。
  •  空間局部性 :在一個(gè)具有良好空間局部性的程序中,如果一個(gè)內(nèi)存位置被引用了一次,那么程序很可能在不遠(yuǎn)的將來(lái)引用附近的一個(gè)內(nèi)存位置。

時(shí)間局部性示例

 
 
 
 
  1. function sum(arry) {  
  2.     let i, sum = 0  
  3.     let len = arry.length  
  4.     for (i = 0; i < len; i++) {  
  5.         sum += arry[i]  
  6.     }  
  7.     return sum  

在這個(gè)例子中,變量sum在每次循環(huán)迭代中被引用一次,因此,對(duì)于sum來(lái)說(shuō),具有良好的時(shí)間局部性

空間局部性示例

具有良好空間局部性的程序

 
 
 
 
  1. // 二維數(shù)組   
  2. function sum1(arry, rows, cols) {  
  3.     let i, j, sum = 0 
  4.     for (i = 0; i < rows; i++) {  
  5.         for (j = 0; j < cols; j++) {  
  6.             sum += arry[i][j]  
  7.         }  
  8.     }  
  9.     return sum  

空間局部性差的程序

 
 
 
 
  1. // 二維數(shù)組   
  2. function sum2(arry, rows, cols) {  
  3.     let i, j, sum = 0  
  4.     for (j = 0; j < cols; j++) {  
  5.         for (i = 0; i < rows; i++) {  
  6.             sum += arry[i][j]  
  7.         }  
  8.     }  
  9.     return sum  

看一下上面的兩個(gè)空間局部性示例,像示例中從每行開(kāi)始按順序訪問(wèn)數(shù)組每個(gè)元素的方式,稱(chēng)為具有步長(zhǎng)為1的引用模式。

如果在數(shù)組中,每隔k個(gè)元素進(jìn)行訪問(wèn),就稱(chēng)為步長(zhǎng)為k的引用模式。

一般而言,隨著步長(zhǎng)的增加,空間局部性下降。

這兩個(gè)例子有什么區(qū)別?區(qū)別在于第一個(gè)示例是按行掃描數(shù)組,每掃描完一行再去掃下一行;第二個(gè)示例是按列來(lái)掃描數(shù)組,掃完一行中的一個(gè)元素,馬上就去掃下一行中的同一列元素。

數(shù)組在內(nèi)存中是按照行順序來(lái)存放的,結(jié)果就是逐行掃描數(shù)組的示例得到了步長(zhǎng)為 1 引用模式,具有良好的空間局部性;而另一個(gè)示例步長(zhǎng)為 rows,空間局部性極差。

性能測(cè)試

運(yùn)行環(huán)境:

  •  cpu: i5-7400
  •  瀏覽器: chrome 70.0.3538.110

對(duì)一個(gè)長(zhǎng)度為9000的二維數(shù)組(子數(shù)組長(zhǎng)度也為9000)進(jìn)行10次空間局部性測(cè)試,時(shí)間(毫秒)取平均值,結(jié)果如下:

所用示例為上述兩個(gè)空間局部性示例

步長(zhǎng)為 1步長(zhǎng)為 9000
1242316

從以上測(cè)試結(jié)果來(lái)看,步長(zhǎng)為 1 的數(shù)組執(zhí)行時(shí)間比步長(zhǎng)為 9000 的數(shù)組快了一個(gè)數(shù)量級(jí)。

總結(jié):

  •  重復(fù)引用相同變量的程序具有良好的時(shí)間局部性
  •  對(duì)于具有步長(zhǎng)為 k 的引用模式的程序,步長(zhǎng)越小,空間局部性越好;而在內(nèi)存中以大步長(zhǎng)跳來(lái)跳去的程序空間局部性會(huì)很差

參考資料:

  •  深入理解計(jì)算機(jī)系統(tǒng)

14. if-else 對(duì)比 switch

當(dāng)判斷條件數(shù)量越來(lái)越多時(shí),越傾向于使用 switch 而不是 if-else。

 
 
 
 
  1. if (color == 'blue') {  
  2. } else if (color == 'yellow') {  
  3. } else if (color == 'white') {  
  4. } else if (color == 'black') {  
  5. } else if (color == 'green') {  
  6. } else if (color == 'orange') {  
  7. } else if (color == 'pink') {  
  8. }  
  9. switch (color) {  
  10.     case 'blue':  
  11.         break  
  12.     case 'yellow':  
  13.         break  
  14.     case 'white': 
  15.         break  
  16.     case 'black':  
  17.         break  
  18.     case 'green':  
  19.         break  
  20.     case 'orange':  
  21.         break  
  22.     case 'pink':  
  23.         break  

像以上這種情況,使用 switch 是最好的。假設(shè) color 的值為 pink,則 if-else 語(yǔ)句要進(jìn)行 7 次判斷,switch 只需要進(jìn)行一次判斷。

從可讀性來(lái)說(shuō),switch 語(yǔ)句也更好。從使用時(shí)機(jī)來(lái)說(shuō),當(dāng)條件值大于兩個(gè)的時(shí)候,使用 switch 更好。

不過(guò),switch 只能用于 case 值為常量的分支結(jié)構(gòu),而 if-else 更加靈活。

15. 查找表

當(dāng)條件語(yǔ)句特別多時(shí),使用 switch 和 if-else 不是最佳的選擇,這時(shí)不妨試一下查找表。查找表可以使用數(shù)組和對(duì)象來(lái)構(gòu)建。

 
 
 
 
  1. switch (index) {  
  2.     case '0':  
  3.         return result0  
  4.     case '1':  
  5.         return result1  
  6.     case '2':  
  7.         return result2  
  8.     case '3':  
  9.         return result3  
  10.     case '4':  
  11.         return result4  
  12.     case '5':  
  13.         return result5  
  14.     case '6':  
  15.         return result6  
  16.     case '7':  
  17.         return result7  
  18.     case '8':  
  19.         return result8  
  20.     case '9':  
  21.         return result9  
  22.     case '10':  
  23.         return result10  
  24.     case '11':  
  25.         return result11  

可以將這個(gè) switch 語(yǔ)句轉(zhuǎn)換為查找表

 
 
 
 
  1. const results = [result0,result1,result2,result3,result4,result5,result6,result7,result8,result9,result10,result11]  
  2. return results[index] 

如果條件語(yǔ)句不是數(shù)值而是字符串,可以用對(duì)象來(lái)建立查找表

 
 
 
 
  1. const map = {  
  2.   red: result0,  
  3.   green: result1,  
  4. return map[color] 

16. 避免頁(yè)面卡頓

60fps 與設(shè)備刷新率

目前大多數(shù)設(shè)備的屏幕刷新率為 60 次/秒。因此,如果在頁(yè)面中有一個(gè)動(dòng)畫(huà)或漸變效果,或者用戶(hù)正在滾動(dòng)頁(yè)面,那么瀏覽器渲染動(dòng)畫(huà)或頁(yè)面的每一幀的速率也需要跟設(shè)備屏幕的刷新率保持一致。

其中每個(gè)幀的預(yù)算時(shí)間僅比 16 毫秒多一點(diǎn) (1 秒/ 60 = 16.66 毫秒)。但實(shí)際上,瀏覽器有整理工作要做,因此您的所有工作需要在 10 毫秒內(nèi)完成。如果無(wú)法符合此預(yù)算,幀率將下降,并且內(nèi)容會(huì)在屏幕上抖動(dòng)。 此現(xiàn)象通常稱(chēng)為卡頓,會(huì)對(duì)用戶(hù)體驗(yàn)產(chǎn)生負(fù)面影響。

假如你用 JavaScript 修改了 DOM,并觸發(fā)樣式修改,經(jīng)歷重排重繪最后畫(huà)到屏幕上。如果這其中任意一項(xiàng)的執(zhí)行時(shí)間過(guò)長(zhǎng),都會(huì)導(dǎo)致渲染這一幀的時(shí)間過(guò)長(zhǎng),平均幀率就會(huì)下降。假設(shè)這一幀花了 50 ms,那么此時(shí)的幀率為 1s / 50ms = 20fps,頁(yè)面看起來(lái)就像卡頓了一樣。

對(duì)于一些長(zhǎng)時(shí)間運(yùn)行的 JavaScript,我們可以使用定時(shí)器進(jìn)行切分,延遲執(zhí)行。

 
 
 
 
  1. for (let i = 0, len = arry.length; i < len; i++) {  
  2.     process(arry[i])  

假設(shè)上面的循環(huán)結(jié)構(gòu)由于 process() 復(fù)雜度過(guò)高或數(shù)組元素太多,甚至兩者都有,可以嘗試一下切分。

 
 
 
 
  1. const todo = arry.concat()  
  2. setTimeout(() => {  
  3.     process(todo.shift())  
  4.     if (todo.length) {  
  5.         setTimeout(arguments.callee, 25)  
  6.     } else {  
  7.         callback(arry)  
  8.     }  
  9. }, 25) 

如果有興趣了解更多,可以查看一下高性能JavaScript第 6 章和高效前端:Web高效編程與優(yōu)化實(shí)踐第 3 章。

參考資料:

  •  渲染性能

17. 使用 requestAnimationFrame 來(lái)實(shí)現(xiàn)視覺(jué)變化

從第 16 點(diǎn)我們可以知道,大多數(shù)設(shè)備屏幕刷新率為 60 次/秒,也就是說(shuō)每一幀的平均時(shí)間為 16.66 毫秒。在使用 JavaScript 實(shí)現(xiàn)動(dòng)畫(huà)效果的時(shí)候,最好的情況就是每次代碼都是在幀的開(kāi)頭開(kāi)始執(zhí)行。而保證 JavaScript 在幀開(kāi)始時(shí)運(yùn)行的唯一方式是使用 requestAnimationFrame。

 
 
 
 
  1. /**  
  2.  * If run as a requestAnimationFrame callback, this  
  3.  * will be run at the start of the frame.  
  4.  */  
  5. function updateScreen(time) {  
  6.   // Make visual updates here.  
  7. }  
  8. requestAnimationFrame(updateScreen); 

如果采取 setTimeout 或 setInterval 來(lái)實(shí)現(xiàn)動(dòng)畫(huà)的話(huà),回調(diào)函數(shù)將在幀中的某個(gè)時(shí)點(diǎn)運(yùn)行,可能剛好在末尾,而這可能經(jīng)常會(huì)使我們丟失幀,導(dǎo)致卡頓。

參考資料:

  •  優(yōu)化 JavaScript 執(zhí)行

18. 使用 Web Workers

Web Worker 使用其他工作線(xiàn)程從而獨(dú)立于主線(xiàn)程之外,它可以執(zhí)行任務(wù)而不干擾用戶(hù)界面。一個(gè) worker 可以將消息發(fā)送到創(chuàng)建它的 JavaScript 代碼, 通過(guò)將消息發(fā)送到該代碼指定的事件處理程序(反之亦然)。

Web Worker 適用于那些處理純數(shù)據(jù),或者與瀏覽器 UI 無(wú)關(guān)的長(zhǎng)時(shí)間運(yùn)行腳本。

創(chuàng)建一個(gè)新的 worker 很簡(jiǎn)單,指定一個(gè)腳本的 URI 來(lái)執(zhí)行 worker 線(xiàn)程(main.js):

 
 
 
 
  1. var myWorker = new Worker('worker.js');  
  2. // 你可以通過(guò)postMessage() 方法和onmessage事件向worker發(fā)送消息。  
  3. first.onchange = function() {  
  4.   myWorker.postMessage([first.value,second.value]);  
  5.   console.log('Message posted to worker');  
  6. }  
  7. second.onchange = function() {  
  8.   myWorker.postMessage([first.value,second.value]);  
  9.   console.log('Message posted to worker');  

在 worker 中接收到消息后,我們可以寫(xiě)一個(gè)事件處理函數(shù)代碼作為響應(yīng)(worker.js):

 
 
 
 
  1. onmessage = function(e) {  
  2.   console.log('Message received from main script');  
  3.   var workerResult = 'Result: ' + (e.data[0] * e.data[1]);  
  4.   console.log('Posting message back to main script');  
  5.   postMessage(workerResult);  

onmessage處理函數(shù)在接收到消息后馬上執(zhí)行,代碼中消息本身作為事件的data屬性進(jìn)行使用。這里我們簡(jiǎn)單的對(duì)這2個(gè)數(shù)字作乘法處理并再次使用postMessage()方法,將結(jié)果回傳給主線(xiàn)程。

回到主線(xiàn)程,我們?cè)俅问褂胦nmessage以響應(yīng)worker回傳的消息:

 
 
 
 
  1. myWorker.onmessage = function(e) {  
  2.   result.textContent = e.data;  
  3.   console.log('Message received from worker');  

在這里我們獲取消息事件的data,并且將它設(shè)置為result的textContent,所以用戶(hù)可以直接看到運(yùn)算的結(jié)果。

不過(guò)在worker內(nèi),不能直接操作DOM節(jié)點(diǎn),也不能使用window對(duì)象的默認(rèn)方法和屬性。然而你可以使用大量window對(duì)象之下的東西,包括WebSockets,IndexedDB以及FireFox OS專(zhuān)用的Data Store API等數(shù)據(jù)存儲(chǔ)機(jī)制。

參考資料:

  •  Web Workers

19. 使用位操作

JavaScript 中的數(shù)字都使用 IEEE-754 標(biāo)準(zhǔn)以 64 位格式存儲(chǔ)。但是在位操作中,數(shù)字被轉(zhuǎn)換為有符號(hào)的 32 位格式。即使需要轉(zhuǎn)換,位操作也比其他數(shù)學(xué)運(yùn)算和布爾操作快得多。

取模

由于偶數(shù)的最低位為 0,奇數(shù)為 1,所以取模運(yùn)算可以用位操作來(lái)代替。

 
 
 
 
  1. if (value % 2) {  
  2.     // 奇數(shù)  
  3. } else {  
  4.     // 偶數(shù)   
  5. }  
  6. // 位操作  
  7. if (value & 1) {  
  8.     // 奇數(shù)  
  9. } else {  
  10.     // 偶數(shù)  

取反

 
 
 
 
  1. ~~10.12 // 10  
  2. ~~10 // 10  
  3. ~~'1.5' // 1  
  4. ~~undefined // 0  
  5. ~~null // 0 

位掩碼

 
 
 
 
  1. const a = 1  
  2. const b = 2  
  3. const c = 4  
  4. const options = a | b | c 

通過(guò)定義這些選項(xiàng),可以用按位與操作來(lái)判斷 a/b/c 是否在 options 中。

 
 
 
 
  1. // 選項(xiàng) b 是否在選項(xiàng)中  
  2. if (b & options) {  
  3.     ...  

20. 不要覆蓋原生方法

無(wú)論你的 JavaScript 代碼如何優(yōu)化,都比不上原生方法。因?yàn)樵椒ㄊ怯玫图?jí)語(yǔ)言寫(xiě)的(C/C++),并且被編譯成機(jī)器碼,成為瀏覽器的一部分。當(dāng)原生方法可用時(shí),盡量使用它們,特別是數(shù)學(xué)運(yùn)算和 DOM 操作。

21. 降低 CSS 選擇器的復(fù)雜性

(1). 瀏覽器讀取選擇器,遵循的原則是從選擇器的右邊到左邊讀取。

看個(gè)示例

 
 
 
 
  1. #block .text p {  
  2.     color: red;  
  1.  查找所有 P 元素。
  2.  查找結(jié)果 1 中的元素是否有類(lèi)名為 text 的父元素
  3.  查找結(jié)果 2 中的元素是否有 id 為 block 的父元素

(2). CSS 選擇器優(yōu)先級(jí)

 
 
 
 
  1. 內(nèi)聯(lián) > ID選擇器 > 類(lèi)選擇器 > 標(biāo)簽選擇器 

根據(jù)以上兩個(gè)信息可以得出結(jié)論。

  1.  選擇器越短越好。
  2.  盡量使用高優(yōu)先級(jí)的選擇器,例如 ID 和類(lèi)選擇器。
  3.  避免使用通配符 *。

最后要說(shuō)一句,據(jù)我查找的資料所得,CSS 選擇器沒(méi)有優(yōu)化的必要,因?yàn)樽盥吐斓倪x擇器性能差別非常小。

參考資料:

  •  CSS selector performance
  •  Optimizing CSS: ID Selectors and Other Myths

22. 使用 flexbox 而不是較早的布局模型

在早期的 CSS 布局方式中我們能對(duì)元素實(shí)行絕對(duì)定位、相對(duì)定位或浮動(dòng)定位。而現(xiàn)在,我們有了新的布局方式 flexbox,它比起早期的布局方式來(lái)說(shuō)有個(gè)優(yōu)勢(shì),那就是性能比較好。

下面的截圖顯示了在 1300 個(gè)框上使用浮動(dòng)的布局開(kāi)銷(xiāo):

然后我們用 flexbox 來(lái)重現(xiàn)這個(gè)例子:

現(xiàn)在,對(duì)于相同數(shù)量的元素和相同的視覺(jué)外觀,布局的時(shí)間要少得多(本例中為分別 3.5 毫秒和 14 毫秒)。

不過(guò) flexbox 兼容性還是有點(diǎn)問(wèn)題,不是所有瀏覽器都支持它,所以要謹(jǐn)慎使用。

各瀏覽器兼容性:

  •  Chrome 29+
  •  Firefox 28+
  •  Internet Explorer 11
  •  Opera 17+
  •  Safari 6.1+ (prefixed with -webkit-)
  •  Android 4.4+
  •  iOS 7.1+ (prefixed with -webkit-)

參考資料:

  •  使用 flexbox 而不是較早的布局模型

23. 使用 transform 和 opacity 屬性更改來(lái)實(shí)現(xiàn)動(dòng)畫(huà)

在 CSS 中,transforms 和 opacity 這兩個(gè)屬性更改不會(huì)觸發(fā)重排與重繪,它們是可以由合成器(composite)單獨(dú)處理的屬性。

參考資料:

  •  使用 transform 和 opacity 屬性更改來(lái)實(shí)現(xiàn)動(dòng)畫(huà) 

網(wǎng)站標(biāo)題:前端性能優(yōu)化23條建議(2020)
轉(zhuǎn)載來(lái)源:http://uogjgqi.cn/article/dpiocdi.html
掃二維碼與項(xiàng)目經(jīng)理溝通

我們?cè)谖⑿派?4小時(shí)期待你的聲音

解答本文疑問(wèn)/技術(shù)咨詢(xún)/運(yùn)營(yíng)咨詢(xún)/技術(shù)建議/互聯(lián)網(wǎng)交流