前端開發效能優化方案

狗尾巴草發表於2018-09-21

減少HTTP請求次數和請求大小

程式碼優化

  • 有利於SEO
  • 有利於擴充套件維護
  • 有利於減少效能消耗
  • [JS程式碼優化的108條建議] [雅虎CSS優化的36條建議] ...

DNS及HTTP通訊方式的優化

1.在JS中儘量減少閉包的使用(原因:閉包會產生不釋放的棧記憶體) A:迴圈給元素做事件繫結的時候,儘可能的把後期需要的資訊(例如索引)儲存到元素的自定義屬性上,而不是建立閉包儲存 B:可以在最外層形成一個閉包,把一些後續需要的公共資訊進行儲存,而不是每一個方法都建立閉包(例如單例模式) C:儘可能的手動釋放不被佔用的記憶體 ...

2.儘量合併CSS和JS檔案(把需要引入的CSS合併為一個,JS也是合併為一個),原理是在減少HTTP請求次數,儘可能的把合併後的程式碼進行壓縮,減小HTTP請求資源的大小 A:webpack這種自動化構建工具,可以幫我們實現程式碼的合併和壓縮(工程化開發) B:在移動開發(或者追求高效能的PC端開發[例如百度首頁]),如果CSS或者JS不是需要很多,我們可以選擇把css和js程式設計內嵌式(也就是程式碼直接寫在HTML中)

3.儘量使用字型圖示或者SVG圖示,來代替傳統的PNG等格式的圖片(因為字型圖示等是向量圖(基於程式碼編寫出來的),放大不會變形,而且渲染速度快,相對比點陣圖要小一些)

4.減少對DOM的操作(主要是減少DOM的重繪和迴流(重排))
A:關於重排的分離讀寫
B:使用文件碎片或者字串拼接做資料繫結(DOM的動態建立)

5.在JS中避免“巢狀迴圈”(這種會額外增加很多迴圈次數)和“死迴圈”(一旦遇到死迴圈瀏覽器就卡殼了)

6.採用圖片的“懶載入”(延遲載入)
目的是為了減少頁面“第一次載入”過程中HTTP的請求次數,讓頁面開啟速度變快
步驟:開始載入頁面的時候,所有的真實圖片都不去傳送HTTP請求載入,而是給一張佔位的背景圖,當頁面載入完,並且圖片在可視區域內我們再去做圖片載入

7.利用瀏覽器和伺服器端的快取技術(304快取),把一些不經常更新的靜態資原始檔做快取處理(例如:JS、CSS、靜態圖片等都可以做快取) 原理是為了減少HTTP請求大小,讓獲取速度更快

8.儘可能使用事件委託(事件代理)來處理事件繫結的操作,減少DOM的頻繁操作,其中包括給每一個DOM元素做事件繫結

9.儘量減少CSS表示式的使用(expression)

#myDiv {
  position: absolute;
  width: 100px;
  height: 100px;
  left: expression(document.body.offsetWidth - 110 + "px");
  top: expression(document.body.offsetHeight - 110 + "px");
  background: red;
}
複製程式碼

10.CSS選擇器解析規則是從右向左解析

 .container .link a{
    先找到所有的A,再篩選是在.link樣式類中的,再次篩選是在.container樣式類中的... 先找到的是所有的A,操作起來是消耗效能的,我們在使用CSS選擇器的時候儘可能減少對標籤選擇器的使用
 }
複製程式碼

11.CSS雪碧圖技術(css sprite / css 圖片精靈) 把所有相對較小資源圖片彙總到一張大圖上,後期我們只需要把大圖載入下來,用背景定位的方式展示對應的小圖即可

.bg{
  background:url('xxx.png');
}
.box1{
   background-position:xx xx;
}
.box2{
   background-position:xx xx;
}

<div class='bg box1'></div>
複製程式碼

13.減少對於COOKIE的使用(最主要的是減少本地COOKIE儲存內容的大小),因為客戶端操作COOKIE的時候,這些資訊總是在客戶端和伺服器端傳來傳去

14.頁面中的資料獲取採用非同步程式設計和延遲分批載入
使用非同步獲取資料,是為了降低HTTP通道的堵塞,不會因為資料沒有請求回來耽誤下面資訊的渲染,提高頁面的開啟速度(我們可以這樣處理:需要動態繫結資料的區域先隱藏,等資料返回並且繫結完成後在讓其顯示)
延遲分批載入類似於圖片懶載入,是為了減少第一次頁面載入時候的HTTP請求次數

15.頁面中出現音視訊標籤,我們不讓頁面載入的時候就去載入這些資源(要不然頁面載入速度會變慢)(方案:只需要設定 preload='none' 即可),等待頁面載入完成,音視訊播放的時候我們在去載入音視訊資源

16.在客戶端和伺服器端進行資訊互動的時候,對於多項資料我們儘可能基於JSON格式來進行傳送(JSON格式的資料處理方便,資源偏小)
==>相對於XML格式的傳輸才會有這個優勢

17.儘可能實現JS的封裝(低耦合高內聚),減少頁面中的冗餘程式碼(減少HTTP請求資源的大小)

20.CSS中設定定位後,最好使用Z-INDEX改變盒子的層級,讓所有的盒子不在相同的平面上,這樣後續處理的時候,效能有那麼一丟丟的提高

21.在基於AJAX的GET請求進行資料互動的時候,根據需求可以讓其產生快取(這個快取不是304快取),這樣下一次從相同地址獲取的資料是上一次快取的資料(但是很少用,專案中一般刻意清除這個快取的時候偏多)

22.儘量減少對於filter濾鏡屬性的使用(這個屬性消耗效能較大一些)

23.在CSS匯入的時候儘量減少使用@import匯入式,因為@import是同步操作,只有把這個對應的CSS匯入,才會向下載入,而link是非同步操作

24.配置ETag(有點類似於304快取)

25.使用window.requestAnimationFrame(JS中的幀動畫)代替傳統的定時器動畫

26.減少遞迴的使用,避免死遞迴,避免由於遞迴導致的棧記憶體巢狀(建議使用尾遞迴)

27.避免使用iframe(不僅不好管控樣式,而且相當於在A頁面中載入了其它頁面,消耗較大)

28.利用H5中提供的localstorage本地儲存或者是manifest離線快取,做一些資訊的本地儲存,下一次載入頁面的時候直接從本地獲取,減少HTTP請求次數

29.基於SCRIPT調取JS的時候,可已使用 defer或者async 來非同步載入

重量級優化:做CDN加速(燒錢機器)

===額外技巧===

1.我們一般都把CSS放到BODY上,把JS放到BODY下面(原因:讓其先載入CSS在載入JS,先載入CSS是為了保證頁面渲染的過程中,元素是帶著樣式渲染的,而JS一般都是用來操作DOM元素的,需要等到元素載入完再操作)

2.能用CSS搞定的絕對不用JS,能用原生JS搞定的絕對不用外掛,絕對不使用FLASH(除了音視訊的低版本瀏覽器播放)
=>CSS處理動畫等功能的效能優於JS,而且CSS中的transform變形還開起了硬體加速

3.JS中儘量減少對EVAL的使用,因為JS合併壓縮的時候,可能出現由於符號不完善,導致的程式碼執行優先順序錯亂問題,EVAL處理起來消耗的效能也是偏大一點的

4.使用keep-alive實現客戶端和伺服器端的長連線

5.儘量使用設計模式來管理我們的程式碼(單例、構造、Promise、釋出訂閱),方便後期的升級和維護

6.開啟伺服器端的gzip壓縮(這個壓縮可以有效減少請求資原始檔的大小),其實客戶端的圖片等資源也是可以進行壓縮的(但是對於24位的點陣圖,壓縮後可能會變模糊)

7.頁面中不要出現無效的連結(利於SEO優化),還有其它技巧:提高關鍵字曝光率、img需要加alt、設定meta標籤、標籤語義化...

8.避免使用with語句(非常耗效能)

============================================

知識點:AJAX

1.async javascript and xml 非同步的JS和XML
在AJAX中的非同步不是我們理解的同步非同步程式設計,而是泛指“區域性重新整理”,但是我們在以後的AJAX請求中儘可能使用非同步獲取資料(因為非同步資料獲取不會阻塞下面程式碼的執行)

XML是一種檔案格式(我們可以把HTML理解為XML的一種):可擴充套件的標記語言,它的作用是用自己擴充套件的一些語義標籤來儲存一些資料和內容,這樣儲存的好處是清晰的展示出資料的結構

很久以前,AJAX剛剛興起的時候,客戶端從伺服器端獲取資料,伺服器為了清晰的表達資料結構,都是返回XML格式的內容,當下,我們獲取的資料一般都是JSON格式的內容,JSON相對於XML來說,也能清晰表達資料結構,而且訪問裡面資料的時候操作起來比XML更簡便(但是現在某些專案中,伺服器返回給客戶端的資料不單純是資料,而是資料和需要展示的結構拼接好的結果(類似於我們自己做的字串拼接),換句話說,是伺服器端把資料和結構拼接好返回給我們,此時返回的資料格式一般都是XML格式的字串)

2.AJAX操作

//=>建立AJAX例項:IE6中是不相容的,使用的是new ActiveXObject來實現的
let xhr = new XMLHttpRequest();

//=>開啟請求:傳送請求之前的一些配置項
//1.HTTP METHOD 請求方式
// GET/DELETE/HEAD/OPTIONS/TRACE/CONNECT
// POST/PUT
//2.URL 向伺服器端傳送請求的API(Application Programming Interface)介面地址
//3.ASYNC 設定AJAX請求的同步非同步,預設是非同步(寫TRUE也是非同步),FALSE是同步,專案中都使用非同步程式設計,防止阻塞後續程式碼執行
//4.USER-NAME/USER-PASS:使用者名稱密碼,一般不用
xhr.open([HTTP METHOD],[URL],[ASYNC],[USER-NAME],[USER-PASS]);

//=>3.事件監聽:一般監聽的都是 READY-STATE-CHANGE 事件(AJAX狀態改變事件),基於這個事件可以獲取伺服器返回的響應頭響應主體內容
xhr.onreadystatechange=()=>{
    if(xhr.readyState===4 && xhr.status===200){
       xhr.responseText;
    }
};

//=>4.傳送AJAX請求:從這步開始,當前AJAX任務開始,如果AJAX是同步的,後續程式碼不會執行,要等到AJAX狀態成功後在執行,反之非同步不會
xhr.send([請求主體內容]);
複製程式碼

3.關於HTTP請求方式的一點學習

所有的請求都可以給伺服器端傳遞內容,也都可以從伺服器端獲取內容
GET:從伺服器端獲取資料(給的少拿的多)
POST:向伺服器端推送資料(給的多拿的少)
DELETE:刪除伺服器端的某些內容(一般是刪除一些檔案)
PUT:向伺服器上存放一些內容(一般也是存放檔案)
HEAD:只想獲取伺服器返回的響應頭資訊,不要響應主體中的內容
OPTIONS:一般使用它向伺服器傳送一個探測性請求,如果伺服器端返回的資訊了,說明當前客戶端和伺服器端建立了連線,我們可以繼續執行其它請求了(TRACE是幹這件事的,但是axios這個AJAX類庫在基於cross domain進行跨域請求的時候,就是先傳送OPTIONS請求進行探測嘗試,如果能連通伺服器,才會繼續傳送其它的請求)

4.GET VS POST

[傳遞給伺服器資訊的方式不一樣]
GET是基於URL地址“問號傳參”的方式把資訊傳遞給伺服器,POST是基於“請求主體”把資訊傳遞給伺服器

  [GET]
  xhr.open('GET','/temp/list?xxx=xxx&xxx=xxx')

  [POST]
  xhr.send('xxx=xxx&xxx=xxx')
複製程式碼

GET一般應用於拿(給伺服器的會少一些),而POST給伺服器的很多,如果POST是基於問號傳參方式來搞會出現一些問題:URL會拼接很長,瀏覽器對於URL的長度有有最大限度(谷歌8KB 火狐7KB IE2KB ...),超過的部分瀏覽器就把它截掉了
=>所以GET請求可以基於URL傳參,而POST都是使用請求主體傳遞(請求主體理論上是沒有限制的,真實專案中我們會自己做大小限制,防止上傳過大資訊導致請求遲遲完不成)

[GET不安全,POST相對安全]

因為GET是基於“問號傳參”把資訊傳遞給伺服器的,容易被駭客進行URL劫持,POST是基於請求主體傳遞的,相對來說不好被劫持;所以登入、註冊等涉及安全性的互動操作,我們都應該用POST請求;

[GET會產生不可控制的快取,POST不會]

不可控:不是想要就要,想不要就不要的,這是瀏覽器自主記憶的快取,我們無法基於JS控制,真實專案中我們都會把這個快取幹掉 GET請求產生快取是因為:連續多次向相同的地址(並且傳遞的引數資訊也是相同的)傳送請求,瀏覽器會把之前獲取的資料從快取中拿到返回,導致無法獲取伺服器最新的資料(POST不會)

解決方案:

  xhr.open('GET',`/temp/list?lx=1000&_=${Math.random()}`); //=>保證每次請求的地址不完全一致:在每一次請求的末尾追加一個隨機數即可(使用_作為屬性名就是不想和其它的屬性名衝突)
複製程式碼

5.AJAX狀態(READY-STATE)

  • 0 =>UNSENT 剛開始建立XHR,還沒有傳送
  • 1 =>OPENED 已經執行了OPEN這個操作
  • 2 =>HEADERS_RECEIVED 已經傳送AJAX請求(AJAX任務開始),響應頭資訊已經被客戶端接收了(響應頭中包含了:伺服器的時間、返回的HTTP狀態碼...)
  • 3 =>LOADING 響應主體內容正在返回
  • 4 =>DONE 響應主體內容已經被客戶端接收

6.HTTP網路狀態碼(STATUS)

根據狀態碼能夠清楚的反映出當前互動的結果及原因

  • 200 OK 成功(只能證明伺服器成功返回資訊了,但是資訊不一定是你業務需要的)

  • 301 Moved Permanently 永久轉移(永久重定向) =>域名更改,訪問原始域名重定向到新的域名

  • 302 Move temporarily 臨時轉移(臨時重定向 =>307) =>網站現在是基於HTTPS協議運作的,如果訪問的是HTTP協議,會基於307重定向到HTTPS協議上 =>302一般用作伺服器負載均衡:當一臺伺服器達到最大併發數的時候,會把後續訪問的使用者臨時轉移到其它的伺服器機組上處理 =>偶爾真實專案中會把所有的圖片放到單獨的伺服器上“圖片處理伺服器”,這樣減少主伺服器的壓力,當使用者向主伺服器訪問圖片的時候,主伺服器都把它轉移到圖片伺服器上處理

  • 304 Not Modified 設定快取 =>對於不經常更新的資原始檔,例如:CSS/JS/HTML/IMG等,伺服器會結合客戶端設定304快取,第一次載入過這些資源就快取到客戶端了,下次再獲取的時候,是從快取中獲取;如果資源更新了,伺服器端會通過最後修改時間來強制讓客戶端從伺服器重新拉取;基於CTRL+F5強制重新整理頁面,304做的快取就沒有用了。

  • 400 Bad Request 請求引數錯誤

  • 401 Unauthorized 無許可權訪問

  • 404 Not Found 找不到資源(地址不存在)

  • 413 Request Entity Too Large 和伺服器互動的內容資源超過伺服器最大限制

  • 500 Internal Server Error 未知的伺服器錯誤

  • 503 Service Unavailable 伺服器超負荷

7.關於XHR的屬性和方法

xhr.response 響應主體內容

xhr.responseText

響應主體的內容是字串(JSON或者XML格式字串都可以) xhr.responseXML 響應主體的內容是XML文件

xhr.status 返回的HTTP狀態碼
xhr.statusText 狀態碼的描述

xhr.timeout 設定請求超時的時間
xhr.withCredentials 是否允許跨域(FALSE)

xhr.abort() 強制中斷AJAX請求
xhr.getAllResponseHeaders() 獲取所有響應頭資訊
xhr.getResponseHeader([key])
獲取KEY對應的響應頭資訊,例如:xhr.getResponseHeader('date')就是在獲取響應有中的伺服器時間

xhr.open() 開啟URL請求
xhr.overrideMimeType() 重寫MIME型別
xhr.send() 傳送AJAX請求
xhr.setRequestHeader() 設定請求頭

相關文章