前端性能優化指南

從代碼優化到網站的性能優化

原文javascript

咱們都知道前端性能很重要,並嘗試從各個方面優化它,但你是否真的知道你網站的性能瓶頸在哪呢?是你的代碼執行效率低下,仍是javascript文件過大,是頻繁的Dom操做,仍是緩慢的渲染速度?最重要的是你應該知道從哪些方面提高網站的性能瓶頸,並長期觀察對比各個方案的效果。css

目錄

  • 代碼優化
    • 高性能代碼
      • 數據存取
      • DOM編程
      • 算法和流程控制
      • ajax
      • 編程實踐
    • 構建優化
      • webpack
      • react
      • vue
  • 網站性能
    • 傳輸過程
      • DNS優化
      • 資源傳輸優化
    • 渲染過程
      • 懶加載
      • 動畫
      • 同構
    • 交互過程
      • dom優化
      • css優化
      • 事件優化

代碼優化

代碼優化包含兩個方面,一是對於JS引擎和瀏覽器而言,高效率的代碼,二是對於不一樣構建環境的代碼,這部分優化針對不一樣的框架而言手段都不盡相同。html

高性能代碼 -來自高性能Javascript一書

1、數據存取前端

  • 使用局部變量:在函數中讀取局部變量是最快的,讀取全局變量是最慢的,由於經過做用域鏈解析標識符須要開銷,而且做用域鏈越長,開銷越大。若是在函數中須要屢次引用全局變量也可優化:
var global = 0;
function() {
    // 在函數內部定義一個變量, 後續直接訪問局部變量便可
    var temp = global;
    temp++;temp--;
    console.log(temp);
}
複製代碼
  • 緩存對象的成員值: 對象的屬性獲取須要必定開銷,且嵌套越深開銷越大,如讀取location.href老是比讀取window.location.href要快,對於須要頻繁訪問的深層對象,須要將其緩存:
var name = element.name;
for(var i = 0; i < 10000; i++) {
    name += "zhou";
    // element.name += "zhou";
}
複製代碼

2、DOM編程vue

  • 建立DOM節點:在大多數狀況下,使用innerHTML要比原生DOM方法快,如createElement
  • querySelect:如querySelectorAll查詢語句性能高於其它的查詢語句,緣由在於querySelect返回的不是動態的HTML集合
  • 離線DOM:對於批量修改DOM樣式時,推薦離線DOM樹,減小訪問DOM佈局的次數。

3、算法和流程控制java

  • 循環
    • for-in循環效率低於for、while, 速度只有for循環的1/7。
    • forEach等基於函數的迭代:forEach會基於數組的每一項調用函數,這是它慢於循環的緣由,在對速度有嚴格要求或數組較長時不推薦使用
    • for循環優化
// 原始版本
for(let i = 0; i < list.length; i++){
    fn(list[i]);
}

// 優化版本1, 緩存list.length,由於length值是不變的
for(let i = 0, len = list.length; i < len; i++){
    fn(list[i]);
}

// 優化版本2,倒序循環,把i的值做爲控制條件,去掉 i < len的比較消耗
for(let i = list.length; i--){
    fn(list[i]);
}
複製代碼
  • 條件語句react

    • 將最可能出現的條件前置以減小判斷的次數
    • 將扁平化的判斷轉換爲嵌套式的判斷,二分法縮小區間,如判斷1-10可轉換爲小於6和大於等於6
    • 經過表查找替代if/else和switch,所謂表查找是將全部狀況的返回值存入數組中訪問的一種方法
  • 遞歸webpack

    • 循環代替遞歸:因爲遞歸調用棧的限制可能會致使棧溢出錯誤,全部可使用循環改寫遞歸
    • 尾遞歸優化:尾遞歸優化不會致使棧溢出,嚴格模式下才有效
    • 開啓緩存:在一次遞歸中可能相同的值會被計算屢次,將結果緩存是優化遞歸效率的手段之一。

4、Ajaxcss3

  • 使用get獲取數據能夠被緩存,只有在url長度大於2048個字符時再考慮post
  • jsonp的方式加載數據是很是快的,由於相應的消息直接做爲js代碼執行,而不是做爲字符串須要進一步處理,但須要注意的是不要使用jsonp去加載不可靠的源數據。
  • 使用圖片向服務器發送信息:具體方式爲,新建一個圖片對象new Image(),並將其src屬性設置爲服務器的urlsrc = url + params,服務器會接受到數據並保存,使用這種方式的消耗是很是小的。
  • 數據格式:json做爲輕量級的數據交換格式是首選,其次是特定字符分割的字符串
  • 使用類庫:合理使用ajax類庫能夠避免在一些古怪的瀏覽器中遇到問題

5、編程實踐web

  • 使用字面量建立對象和數組: var a = [], b = {},能夠在控制檯中查看效率
  • with和eval:避免使用這兩種語句,由於它們會形成動態做用域問題且性能較低。
  • 避免重複工做:不少次的代碼重構其實都在在消除重複,儘可能在開發階段處理好,不要想着重構是再來優化
  • 經過位運算提示計算效率:如,經過對整數求模的方式爲表格添加條紋,使用i % 2 === 1判斷, 這個計算可轉換爲位運算提高效率: i & 1
  • 在進行數學運算:使用Math提供的方法要比本身寫執行效率高,儘可能使用原生方法
  • 使用性能分析工具:如chrome的devTools分析性能瓶頸

構建優化

1、webpack

  • 代碼分離
    • 多入口分離
    • 複用組件分離 (CommonsChunkPlugin)
    • 動態導入組件分離 (import())
  • Loader
  • resolve(解析路徑) & Externals(外部擴展)
  • Dll優化
  • source map
  • tree shaking
  • Split CSS(分離css)
  • webpack性能優化

2、React

  • shouldComponentUpdate:合理使用shouldComponentUpdate阻止組件更新能夠有很大的性能優化(或使用PureComponent)
  • 爲列表設置惟一key:設置惟一key可讓diff算法在對比同一級元素變更時有更好的表現(不用頻繁的刪除和添加元素,而是移動元素位置)
  • React Fragments:避免額外標記,由於每一個組件須要有單一根元素,因此常常新增額外標籤,使用React Fragments提供的一組空元素解決: <></>
  • 不使用內聯函數:內聯函數每次調用「render」函數時都會建立一個新的函數實例
// 一、使用內聯函數
render() {
    return (
   	<div onClick={e => this.handleClick(e)}>test</div>
    )
}

// 二、不使用內聯函數
render() {
    return (
   	<div onClick={this.handleClick}>test</div>
    )
}
複製代碼
  • componentWillMount:在react16更新後,render以前的生命週期有可能會執行屢次(React Fiber的影響),而且在react17中也會刪除今生命週期,建議不要使用。重複執行也是getDerivedStateFromProps鉤子被設計爲static函數的緣由 參考react16的更新
  • 優化條件渲染:不少狀況React不須要徹底卸載一個組件,如動態切換顯示和隱藏,這時咱們能夠考慮用樣式切換來代替條件渲染(相似於vue的v-if和v-show)。
  • componentDidCatch:爲組件建立錯誤邊界,componentDidCatch鉤子能捕獲到組件自己render方法和子組件的生命週期方法拋出的錯誤,爲應用提供一個錯誤收集的方式和兜底的設計。
  • 不要使用index做爲key:在某些狀況下使用index做爲key會致使更糟糕的性能,如在列表頭部新增一個項,這會致使列表全部項被從新添加。相同的道理,不要作出將列表尾部的項移動到列表頭部等操做。

3、Vue

  • v-if / v-show: 合理使用v-if / v-show
  • 列表元素設置key(同react)
  • keep-alive組件緩存
  • data優化:對於不須要被監聽的對象不要放在data中,或者使用Object.freeze()凍結對象。
  • props: 定義儘可能詳細的props, 避免這樣使用props: ["status"]
  • v-if和v-for: v-if和v-for不要用在一塊兒,好的作法是使用計算屬性代替

網站性能優化

一般,網站的性能指標反應在網站加載時間,和後續交互體驗上。網站從加載到用戶可交互的時間有多個過程時間和。

傳輸過程

用戶在訪問網站過程當中,會有DNS解析,TCP握手,HTTP資源傳輸等過程, 針對這些過程優化以下。

  • DNS預解析,對靜態資源域名添加dns-prefetch

    • a標籤的href在各大主流瀏覽器中會自動dns-prefetch,可是https域名不會。
    • 添加meta頭的方式解決https域名不會自動預解析: <meta http-equiv="x-dns-prefetch-control" content="on">
    • 使用場景一般是網站中包含了大量的外部資源,<link rel="dns-prefetch" href="//wddsss.com/a.jpg">
  • HttpDns防劫持 DNS劫持是運營商的DNS服務在解析後返回了不正確的主機IP,將用戶導入到其餘網頁的現象,可使用HttpDns(某雲有提供此服務)。

    image.png
    使用HttpDns eg:http://127.0.0.1?domain=https://www.wddsss.com

  • CDN加速資源傳輸

    • 物理層的硬件優化:更快的硬盤讀取,更高的網絡帶寬
    • 網絡層的尋址優化:尋找距離最近的資源,依賴於網絡層的路由協議尋址算法
    • 傳輸層的優化:1: TCP的慢啓動流量控制能夠用於避免網絡擁塞,2: 設置rwnd爲一合理的值提高最大吞吐量(針對不一樣類型的流量,rwnd的值設置應該不盡相同)
    • 應用層的緩存優化:資源合理設置緩存
  • SSL加速

    • 減小中間證書:客戶端經過https請求服務端,服務端返回證書信息給客戶端,客戶端須要驗證證書是否可信(瀏覽器的可信CA列表驗證),若是證書的頒發機構不在瀏覽器可信列表中,則會檢查此CA的上層機構是否可信,直到找到可信CA。經過減小中間證書機構優化證書驗證效率
    • OCSP Stapling:OCSP是一個TLS證書狀態查詢擴展,它加速上述查詢CA過程,能夠在服務端配置。
  • Http/2優化資源傳輸

    • 多路複用:一個域名維護一個TCP連接,http請求以流的方式傳輸,實現資源並行下載
    • 頭部壓縮:使用靜態表和動態表壓縮http的頭部信息
  • 資源壓縮

    • Brotli壓縮算法(用於純文本): 由Google推出的無損壓縮算法。Brotli 經過變種的LZ77算法、Huffman 編碼以及二階文本建模等方式進行數據壓縮,與其餘壓縮算法相比,它有着更高的壓縮效率。對於常見的純文本,Brotli壓縮性能比gzip提升了17-25%(IE不支持)
    • gzip:gzip能夠對全部常見web資源壓縮,瀏覽器支持性也很好,幾乎全部瀏覽器都支持
  • 圖片優化

    • 響應式圖片:在不一樣環境不一樣場景中使用不一樣size或者不一樣質量的圖片,通常CDN圖片都會提供此功能。
    • webp圖片支持:對於支持的瀏覽器返回webp格式的圖片,注意兩個問題,一是向下兼容,對於不支持webp的瀏覽器須要返回可用格式的圖片,二是服務端緩存(memcahe經過vary字段緩存多份html文檔解決) 詳細請查看全站webp支持
    • gif轉循環視頻:在瀏覽器中gif的表現不理想,可使用循環播放的視頻代替
    • save-date: 在http請求頭中新增save-data: on,獲取壓縮後的圖片(須要服務端支持)
  • 資源緩存

    • 強緩存:由http頭信息的Expires/Cache-Control控制,當資源處於強緩存未過時狀態,資源的狀態碼是 200(memory cache or disk cache)
    • 協商緩存: 由http頭信息的 ETag/If-None-Match、last-modified/if-modified-since控制,協商緩存未過時狀態碼是 304(Not Modified),注意過分使用Etag計算文件hash值會增大服務器壓力,增長文件相應時間,須要謹慎使用。
    • service workers:使用service workers緩存靜態資源甚至整個頁面

渲染過程

  • 下載js文件
    • 普通js:同步下載js文件,下載完成後當即執行
    • async:異步下載js文件,下載完畢後當即執行
    • defer:異步下載js文件,在文檔加載完畢,DOMContentLoaded事件觸發以前執行
    • 動態下載:在js文件中添加一個script標籤,併爲其添加src屬性,當次標籤被添加到dom時開始下載,推薦使用。
    • ajax下載:在ajax中獲取js文件內容,並設置script.text = rs.jsContent,經過此方式添加的js的主要優勢是你能夠下載js代碼但不當即執行。

async和defer都能異步下載js文件,作到了js下載的同時不阻塞文檔解析,可是async因爲其加載完成後當即執行的特性,致使js的執行順序沒法控制,因此實戰中推薦使用defer。

  • 懶加載:
    • webpack配置路由懶加載:依賴於import()或require.ensure()
    • 組件懶加載:一樣依賴於import()或require.ensure()的動態導入
    • IntersectionObserver構造函數: IntersectionObserver爲開發者提供了一種能夠異步監聽目標元素與其祖先或視窗(viewport)交叉狀態的手段,說白了就是提供了一種監聽目標元素是否在可視區域(viewport)內的api,咱們可使用它實現懶加載,以圖片懶加載爲例:
const io = new IntersectionObserver(callback);
let imgs = document.querySelectorAll('[data-src]')
imgs.forEach((item)=>{
    io.observe(item)
})
function callback(items){
    items.forEach(item => {
        if(item.isIntersecting){ // 當前元素可見
            item.target.src = item.target.dataset.src
            io.unobserve(item.target)
        }   
    })
}
複製代碼

這裏圖片的初始src能夠設置一個質量很低的圖片,以達到圖片加載的流暢度。

  • 預加載

    • preconnet預鏈接:瀏覽器對於要請求的資源都須要進行DNS查詢,TCP鏈接,TSL認證過程,使用preconnet能夠預先創建鏈接,在須要使用時直接獲取資源。<link preconnet href="https://www.wddsss.com/a.js"/>
    • prefetch預加載:prefetch可以讓瀏覽器預加載一個資源<link prefetch href="https://www.wddsss.com/a.html" />
    • prerender預渲染: 瀏覽器不只會下載資源,還會分配部分資源對其渲染,以到達用戶下個頁面響應速度更快,達到秒開的目的。
  • css優先下載

    • 將首屏使用的css放置在head中提早下載,放置由於css下載過慢致使頁面閃爍。
    • 將重要的css代碼內嵌在html文檔中,缺點是耦合性高,不利於緩存
    • 使用HTTP/2服務端推送傳遞重要的css
  • 骨架屏:骨架屏就是在頁面數據還沒有加載前先給用戶展現出頁面的大體結構,經常使用在比較規則的列表頁面,能夠本身設計也可以使用已有的解決方案。

  • 動畫優化

    • translate3d:開啓translate3d可讓GPU參與加速動畫渲染,這是一種欺騙瀏覽器的hack方法,讓瀏覽器認爲即將渲染3D動畫,其實元素根本沒有在z軸運動。
    • will-change:will-change是css3新增的屬性,和translate3d相似,是一種加速動畫渲染的方法,js在異步事件中觸發瀏覽器大量繪製界面時,瀏覽器每每是沒有準備的被動使用cpu去計算頁面渲染,而will-change能夠提早告知瀏覽器此元素的渲染須要gpu參與高速渲染。用法:will-change: transform, will-change: contents
    • 使用高性能動畫屬性:如transformopacity等,減小使用box-shadow等消耗較大的屬性動畫。
    • 減小回流的動畫:元素的某些屬性在發生變化後會致使瀏覽器大面積重繪頁面,視覺上反應爲動畫卡頓。
    • js的執行和渲染互斥:js在執行期間將主線程中的渲染操做收集起來,並在本輪事件循環結束(微任務執行完畢)後執行全部渲染操做,可是若是在js中獲取佈局信息則會打亂這裏過程,迫使瀏覽器將暫存的全部渲染操做優先執行,而後獲取最新的渲染狀態,如獲取元素的offsetTop、clientTop等。
    • requestAnimationFrame:requestAnimationFrame是H5新增的api,傳統的js實現動畫在setInterval中實現,setInterval中的代碼並非嚴格意義上的定時執行,有可能形成性能問題,requestAnimationFrame則充分利用了屏幕的刷新機制,可使用此API代替setInterval
  • 服務端渲染

交互過程

  • web worker: 使用web woker處理耗時操做,在執行完成後將結果通知主線程便可,web woker優化可讓用戶的交互及時響應,不至於被耗時的js執行阻止界面渲染。

  • DOM

    • 緩存dom引用:將獲取的dom元素賦值給變量,不用重複獲取dom對象
    • 避免在循環中操做dom
    • documentFragment:合併DOM操做
    • class替換:對於元素上樣式變化較多的操做能夠考慮使用class替換
    • 使用框架:如今前端流行框架都有本身的dom處理方案,建議在理解其原理的同時合理的使用。
  • CSS

    • flexbox:flexbox佈局代替浮動佈局
    • calc:非特殊狀況不使用計算屬性
    • 選擇器:優先使用class選擇器,避免選擇器嵌套過深
    • @import:避免使用@import,@import引入的css下載優先級較低,而且兼容性有必定問題。
    • 不用ID選擇器:避免使用ID選擇器聲明樣式
  • 事件

    • 防抖和節流
    • 事件委託:相比於爲每一個元素綁定事件,事件委託能夠減小網站內存消耗,不然在一些低端機型上可能成爲性能瓶頸。

說在最後

本次的優化指南從代碼的執行到網站的加載,從構建的優化到資源傳輸,羅列了一組優化清單,你能夠經過逐條對比的方式找到本身網站的優化空間。因爲篇幅問題,其中多數內容都只是總結輸出一筆帶過,並不涉及具體如何優化細節,能夠自行經過本身搜索或超連接閱讀。

性能的優化應該始於一個完善的檢測系統,而且有健全的度量過程,且必需要清楚,對於網站的一次優化重構,具體帶來了怎樣的性能提高?整個優化過程最好是一個有計劃且有目標的,你須要清楚這次優化的目的,和目標性能的提高,這不是盲目的。

權衡你的站點,權衡時間和收益,首先列出優先級最高的優化清單,而後就去作吧!

最後給你們推薦一網站,熱點檢索專用,站長高產似那啥,你想看的站點都有。戳我查看

相關文章
相關標籤/搜索