web前端性能優化

 代碼:https://github.com/zhaobao1830/xingnengyouhuajavascript

一、web前端本質上是一種前端GUI(圖形界面系統)軟件,本能夠直接借鑑其餘GUI系統架構設計方法。但web前端有點特別php

 

B/Scss

 

 二、瀏覽器的一個請求從發送到返回都經歷了什麼?html

 

 

 

  1. 首先作 DNS 查詢,若是這一步作了智能 DNS 解析的話,會提供訪問速度最快的 IP 地址回來
  2. 接下來是 TCP 握手,應用層會下發數據給傳輸層,這裏 TCP 協議會指明兩端的端口號,而後下發給網絡層。網絡層中的 IP 協議會肯定 IP 地址,而且指示了數據傳輸中如何跳轉路由器。而後包會再被封裝到數據鏈路層的數據幀結構中,最後就是物理層面的傳輸了
  3. TCP 握手結束後會進行 TLS 握手,而後就開始正式的傳輸數據
  4. 數據在進入服務端以前,可能還會先通過負責負載均衡的服務器,它的做用就是將請求合理的分發到多臺服務器上,這時假設服務端會響應一個 HTML 文件
  5. 首先瀏覽器會判斷狀態碼是什麼,若是是 200 那就繼續解析,若是 400 或 500 的話就會報錯,若是 300 的話會進行重定向,這裏會有個重定向計數器,避免過屢次的重定向,超過次數也會報錯
  6. 瀏覽器開始解析文件,若是是 gzip 格式的話會先解壓一下,而後經過文件的編碼格式知道該如何去解碼文件
  7. 文件解碼成功後會正式開始渲染流程,先會根據 HTML 構建 DOM 樹,有 CSS 的話會去構建 CSSOM 樹。若是遇到 script 標籤的話,會判斷是否存在 async 或者 defer ,前者會並行進行下載並執行 JS,後者會先下載文件,而後等待 HTML 解析完成後順序執行,若是以上都沒有,就會阻塞住渲染流程直到 JS 執行完畢。遇到文件下載的會去下載文件,這裏若是使用 HTTP 2.0 協議的話會極大的提升多圖的下載效率。
  8. 初始的 HTML 被徹底加載和解析後會觸發 DOMContentLoaded 事件
  9. CSSOM 樹和 DOM 樹構建完成後會開始生成 Render 樹,這一步就是肯定頁面元素的佈局、樣式等等諸多方面的東西
  10. 在生成 Render 樹的過程當中,瀏覽器就開始調用 GPU 繪製,合成圖層,將內容顯示在屏幕上了

 

 

 三、請求過程當中一些潛在的性能優化點前端

      dns是否能夠經過緩存減小dns查詢時間?vue

      網絡請求的過程走最近的網絡環境?java

      相同的靜態資源是否能夠緩存?node

      可否減小請求http請求大小? react

     減小http請求(將多個js文件合併)webpack

     服務端渲染

 

具體來講,DNS 解析花時間,能不能儘可能減小解析次數或者把解析前置?能——瀏覽器 DNS 緩存和 DNS prefetch。TCP 每次的三次握手都急死人,有沒有解決方案?有——長鏈接、預鏈接、接入 SPDY 協議。若是說這兩個過程的優化每每須要咱們和團隊的服務端工程師協做完成,前端單方面能夠作的努力有限,那麼 HTTP 請求呢?——在減小請求次數和減少請求體積方面,咱們應該是專家!再者,服務器越遠,一次請求就越慢,那部署時就把靜態資源放在離咱們更近的 CDN 上是否是就能更快一些?

以上提到的都是網絡層面的性能優化。再往下走就是瀏覽器端的性能優化——這部分涉及資源加載優化、服務端渲染、瀏覽器緩存機制的利用、DOM 樹的構建、網頁排版和渲染過程、迴流與重繪的考量、DOM 操做的合理規避等等——這正是前端工程師能夠真正一展拳腳的地方。學習這些知識,不只能夠幫助咱們從根本上提高頁面性能,更可以大大加深我的對瀏覽器底層原理、運行機制的理解,一箭雙鵰!

 

 四、Gzip 壓縮原理

說到壓縮,可不僅是構建工具的專利。咱們平常開發中,其實還有一個便宜又好用的壓縮操做:開啓 Gzip。

具體的作法很是簡單,只須要你在你的 request headers 中加上這麼一句:

accept-encoding:gzip

HTTP 壓縮是一種內置到網頁服務器和網頁客戶端中以改進傳輸速度和帶寬利用率的方式。在使用 HTTP 壓縮的狀況下,HTTP 數據在從服務器發送前就已壓縮:兼容的瀏覽器將在下載所需的格式前宣告支持何種方法給服務器;不支持壓縮方法的瀏覽器將下載未經壓縮的數據。最多見的壓縮方案包括 Gzip 和 Deflate。... https://juejin.im 掘金 — 一個幫助開發者成長的社區

HTTP 壓縮就是以縮小體積爲目的,對 HTTP 內容進行從新編碼的過程

Gzip 的內核就是 Deflate,目前咱們壓縮文件用得最多的就是 Gzip。能夠說,Gzip 就是 HTTP 壓縮的經典例題。

該不應用 Gzip

若是你的項目不是極端迷你的超小型文件,我都建議你試試 Gzip。

有的同窗或許存在這樣的疑問:壓縮 Gzip,服務端要花時間;解壓 Gzip,瀏覽器要花時間。中間節省出來的傳輸時間,真的那麼可觀嗎?

答案是確定的。若是你手上的項目是 1k、2k 的小文件,那確實有點高射炮打蚊子的意思,不值當。但更多的時候,咱們處理的都是具有必定規模的項目文件。實踐證實,這種狀況下壓縮和解壓帶來的時間開銷相對於傳輸過程當中節省下的時間開銷來講,能夠說是微不足道的。

Gzip 是萬能的嗎

首先要認可 Gzip 是高效的,壓縮後一般能幫咱們減小響應 70% 左右的大小。

但它並不是萬能。Gzip 並不保證針對每個文件的壓縮都會使其變小。

Gzip 壓縮背後的原理,是在一個文本文件中找出一些重複出現的字符串、臨時替換它們,從而使整個文件變小。根據這個原理,文件中代碼的重複率越高,那麼壓縮的效率就越高,使用 Gzip 的收益也就越大。反之亦然。

webpack 的 Gzip 和服務端的 Gzip

通常來講,Gzip 壓縮是服務器的活兒:服務器瞭解到咱們這邊有一個 Gzip 壓縮的需求,它會啓動本身的 CPU 去爲咱們完成這個任務。而壓縮文件這個過程自己是須要耗費時間的,你們能夠理解爲咱們以服務器壓縮的時間開銷和 CPU 開銷(以及瀏覽器解析壓縮文件的開銷)爲代價,省下了一些傳輸過程當中的時間開銷。

既然存在着這樣的交換,那麼就要求咱們學會權衡。服務器的 CPU 性能不是無限的,若是存在大量的壓縮需求,服務器也扛不住的。服務器一旦所以慢下來了,用戶仍是要等。Webpack 中 Gzip 壓縮操做的存在,事實上就是爲了在構建過程當中去作一部分服務器的工做,爲服務器分壓。

所以,這兩個地方的 Gzip 壓縮,誰也不能替代誰。它們必須和平共處,好好合做。做爲開發者,咱們也應該結合業務壓力的實際強度狀況,去作好這其中的權衡。

四、網絡請求的時候,會出現網絡選擇和緩存的問題,咱們通常用cdn,可是cdn也有一個問題,cdn是請求靜態資源用的,請求中攜帶的cookie是沒有用的,因此咱們但願在請求的過程當中把cookie從http.request.header中去掉,可是不少時候cdn的域名會弄得和自己網站的域名相同,就會將咱們主站的cookie經過網絡去攜帶到咱們的cdn的服務端,這個是對網絡無謂的損耗,因此咱們必定要注意,cdn的域名不要和主站的域名同樣,這樣就能夠防止訪問cdn的時候,還攜帶主站cookie這個問題

五、經過瀏覽器的緩存策略,咱們能夠對相同的資源和相同的接口從瀏覽器的緩存中讀取數據

六、如今流行的一些框架,好比:vue、react都是在瀏覽器端渲染,不是直接顯示html,是走框架中的代碼渲染,這個渲染過程對於首屏就有很大的損耗,不利於前端的性能的,這種場景下,咱們就有一個服務端渲染的方案,在服務端進行整個html的渲染,從而將整個html指出到瀏覽器端。因此咱們能夠作一些服務器端渲染優化的方案

七、

8

九、

js壓縮的意義:減小網路請求的大小;瀏覽器併發請求的數量是有限的,壓縮之後,減小請求數量

 十、沒有合併文件會出現的問題

十一、

文件合併之後,若是頁面顯示,須要JS文件支持,那就要等這個合併之後的JS請求完成,首屏渲染會須要很長時間。這種狀況容易出如今使用框架的項目中,好比vue、react,

項目要想正常運行,就須要加載完全部文件,解決辦法是使用服務器端渲染

緩存失效問題,多個js文件合併,只要有一個js文件被修改,合併文件就會被修改,以前的緩存就失效了

真實環境中,公共庫不容易修改,業務代碼頻繁修改,因此把公共庫單獨打包成一個文件,業務代碼單獨打包成一個文件,這樣業務代碼修改的時候,也不會影響公共庫代碼的緩存

不一樣頁面的合併,就是單頁應用中,每一個頁面的展現,都是請求當前頁面的JS,因此咱們把每一個頁面的js單獨打包,在加載這個頁面的時候,才加載對應的js。如今的技術是能夠實現的,異步加載組件

十二、

 

 

 

雪碧圖,PC端用的多

缺點:在整個雪碧圖加載完以前,裏面的image都不能使用(能夠拆分雪碧圖)

 

若是頁面中引入的圖片比較小,那就把圖片內容內嵌到html中,用base64的格式文件信息inline到html中,由於這是性能的消耗主要就是在請求圖片中消耗的,若是有10個圖,不可能去請求10次,最好的辦法是把圖片內容inline到html中,一塊兒請求,這樣能夠減小請求數量

 

 在作一些相對簡單的頁面的時候,用矢量圖,用的是svg標籤繪製一個矢量圖,不須要通過http請求加載資源,同時也在整個dom樹簡析的時候就將這個svg進行渲染

 十一、

併發加載:html在加載頁面的時候,會引入css和js,瀏覽器在同一個域名下的加載數量是有限的,因此實際狀況下,咱們會引入3到4個cdn,提升瀏覽器的併發請求上限

依賴關係:css要放在header中,否則會出如今渲染過程當中,頁面忽然閃一下

引入方式:Js:第一種:直接用script引入,會出現阻塞;第二種:動態引入,加載到這個頁面的時候才引入,這個主要是單頁應用中

詞法分析:瀏覽器對html簡析的方式,從上到下,順序執行

併發加載:html引入的資源是併發去請求的

併發上限:瀏覽器在同一個域名下併發請求是有上限的,咱們應該控制某個域名下資源請求的數量,從而避免併發上限

十二、

css在head中經過link引入,會阻塞頁面的渲染

css不阻塞外部腳本的加載,可是會阻塞js的執行,好比js裏須要動態改變css的樣式

1三、

直接引入的js阻塞頁面的渲染,例如js會調用document.write這種方式,來改變dom結構,這時渲染會暫停

defer不會阻塞頁面渲染,是在dom樹加載完成後再加載這個js,並且這些js的加載也是按順序加載的

async不會阻塞頁面渲染,不是按順序加載js的,哪一個Js先返回,就加載誰。不會關心依賴關係,也不要有依賴關係,有依賴關係的腳本不要經過async引入

defer能保證dom樹必定已經加載完整,只用script或者async不必定能保證

 

1四、link和@import的區別

低版本瀏覽器中:

   link支持併發,@import不支持併發,一個@import文件加載完後,纔會加載另外一個@import文件

  整個頁面加載完成後,纔會執行@import中的代碼

上面的倆條在高版本瀏覽器中已經沒有了,@import也支持併發

@import的一個優勢是:適合模塊化開發,能夠在css文件中引入另外一個css文件(注意:這種狀況下,只有前一個css文件執行完,纔會執行引入的這個css文件,因此要避免多層引入)

1五、懶加載

 

    圖片進入可視區域以後纔會請求圖片資源,以前圖片的src爲一個1kb的圖片路徑,真實的url值保持在data-url中,經過監聽scroll事件,當觸發的時候,將img標籤上的data-url屬性的值放到src中,這時src的變化就會觸發相關資源的請求

    懶加載多用於電商網站中

    使用的緣由是由於:一個電商網站中有多個圖片,有時候看瀏覽網站的時候,不會都看,若是把沒有進入可視區域的圖片也加載,會形成資源的浪費。還有一個緣由是:在網頁中,img通常都在js上方,src請求的資源的時候,會佔據大量的併發,進而影響Js的加載,影響網站的正常使用

 

在有依賴關係的場景可使用預加載,好比這個頁面須要這個圖片,只有這個圖片加載完之後,頁面纔會顯示

使用場景:抽獎

 1六、css性能會讓javascript變慢?

  加載css的時候,會阻塞渲染;加載js的時候,也會阻塞渲染

  在瀏覽器中,js解析和ul渲染是在單獨的線程中,一個線程=》javascript解析,一個線程=》ui渲染,這倆個線程是互斥的,瀏覽器是單線程運行機制,css會影響頁面的樣式的展現,若是渲染的流出頻繁的觸發的話,就是頻繁觸發重繪與迴流,會致使UI頻繁渲染,阻塞javascript的線程,最終致使JS變慢

   咱們應該優化css寫法, 從而讓瀏覽器UI渲染的次數和難度下降,從而來加快渲染的速度,讓整個性能有所提高

 1七、避免重繪和迴流的方法

缺點:合成圖層的時候,會大量消耗資源

 1八、

會破壞緩存機制

二、opacity替代visibility(這個須要讓dom是獨立的圖層):https://coding.imooc.com/lesson/130.html#mid=6521

五、能夠前面定義一個變量,把獲取dom屬性的值賦給這個變量,而後使用這個變量進行操做

 

1九、瀏覽器存儲--cookies

Cookie的生成方式:

    一、服務器端生成,http response header中的set-cookie

    二、經過document.cookie能夠讀寫cookie,

         獲取的是cookie鍵值對組成的字符串:

         

         寫的時候,能夠用下面這個方法:

           

           

 

Cookie的做用:

    一、用於瀏覽器端和服務器端的交互

     二、客戶端自身數據的存儲

Cookie有個很重要的屬性:過時時間--expire

Cookie另外一個個很重要的屬性: httponly,設置之後,js沒法讀取和修改cookie

Cookie存儲的限制:

      一、 做爲瀏覽器存儲,大小4KB左右

       二、須要設置過時時間--expire

 在實際的項目中,Cookie存儲數據能力被localstorage所替代

cdn請求靜態資源的時候,會把Cookie帶上,這樣會消耗大量的流量

 

20、

實例:

想localStorage中存儲數據  localStorage.setItem('key','value')

 

從localStorage中獲取數據   loaclStorage.getItem('name') 

 

 

 2一、

 實例:

 

 

2二、

PWA是谷歌開發的,是一個漸進式的Web,舉個例子:手機在網很差的狀況下如何快速顯示內容,離線狀態下該如何,1G下如何,2G下如何,4G下如何

 

檢測是不是PWA,以及網站性能

能夠理解爲主線程外的另外一個線程,如今的javascript是單線程的,全部的加載都在主線程中執行,js主線程能夠把一些耗時比較長,不依賴頁面或用戶交互的特性放到這個

Service Worker中,到瀏覽器的後臺去運行,這樣能夠減小主線程被阻塞的程度,同時豐富瀏覽器對後續特性的擴展

Service Worker倆個普遍的應用點:

1、Service Worker能夠用來實現離線加載:

   在沒有網的狀況下,發出請求,請求被Service Worker攔截,Service Worker可使用它的緩存數據進行頁面加載,實際上這個時候並無把請求打到網絡上,因此這時就算

沒有網絡,頁面也能夠渲染出來,後面若是有網了,就能夠用請求到的資源來替換Service Worker渲染的dom

2、能夠把一些須要消耗很長時間運算的,不影響頁面渲染的功能放到後臺進行執行,執行完之後,經過Service Worker與主頁面有關的運算機制,傳遞給主頁面,而後主頁面

監聽相關message的事件,就能拿到Service Worker的運算結果,從而進行頁面後續邏輯的執行

 

Service Worker只能在https下用,本地開發中沒有https,可使用localhost,不能使用ip

 

2三、http header端的緩存

      Cache-Control

no-cache 瀏覽器請求會到服務器端,而後使用例如Last-Modified等方法進一步判斷當前瀏覽器端的緩存有沒有過時

no-store 沒有緩存

max-age的優先級高於Expires

max-age和Expires都是強瀏覽器端緩存行爲,意思就是這倆個屬性生效的時候,不會觸發向服務器端發起請求的過程,而是直接從咱們的瀏覽器中讀相應緩存的數據。這會有一個問題:當服務端有文件更新了,客戶端是不知道的,這時候就須要Last-Modified/If-Modified-Since

流程是這樣的:客戶端向服務器端請求,服務端返回的Response Headers裏面有last-modified,高數客戶端這個資源修改的最後時間是何時;客戶端再請求的時候,服務端就會

查看Request Headers裏面有沒有if-modified-since,若是有,就進行判斷,看這個時間點以後有沒有修改資源,沒有的話,就返回304,讓客戶端請求客戶端的緩存資源,有的話,就返回200,而且更新last-modified。

實際工做中,last-modified會和cache-control配合使用,若是cache-control設置了緩存,就會先使用客戶端的緩存資源,過時之後,纔會想服務器端發出請求

 

is-modified是以秒爲單位的,若是是在毫秒裏面修改了,它是察覺不到的

2四、爲了解決上面提到的last-modified的肯定,可使用下面的

原理仍是同樣,服務器端的http response header裏面攜帶ETag到客戶端,客戶端請求的時候,會在Request Headers裏面攜帶if-None-Match進行判斷,若是match,就使用緩存,不然向客戶端發起請求

從下往上,一層一層

在第二層,ETag的優先級比if-modified高

上面這個圖的原理是:

最上層:當第一次請求的時候,直接向服務端發起請求,返回的狀態碼是200

第二層:當下一層的緩存時間失效,就進入這一層,通過先後端協商,而後進行判斷,若是後端沒有修改數據,就使用前端緩存,返回的狀態碼是304

最下層:因爲設置了緩存,因此請求的時候,使用的是瀏覽器緩存的資源,返回的狀態碼是200(from cache)

備註:ETag和last-modified都是後端tomcat或nagix設置

 

2五、

上面圖的理解:用戶從瀏覽器端發出請求,判斷是否存在緩存,若是沒有,就向服務端發起請求,服務端會請求響應,並把緩存策略告訴客戶端,經過http response headers裏的catche-control、Last-Modified、Etag來進行緩存協商,最終展現咱們的資源。

若是存在緩存,就會簡析這個緩存文件,檢查緩存文件是否已經在本地過時,經過max-age、Expires判斷,若是緩存沒有過時,就直接使用緩存內容,返回相關的資源展現

若是緩存時間已通過期,就會去找Etag(優先級高),在http request headers裏帶上if-none-match向服務器端發起請求,到了服務器端,會進行判斷,這個if-none-match的hash值和我服務器端的hash值是否一致,若是相同,返回304,瀏覽器會去取緩存文件。若是倆個hash值不一致,就會返回200,而後返回新的數據,而且協商新的緩存機制

若是沒有Etag,就走last-modified分支,以後和上面的一致

 

Cache-Control:

是否能夠重用,若是不能夠,jiushino-store

若是能夠,就判斷每次請求是否從新驗證,若是yes,就是no-cache,不走三層策略裏面的最下層,都會走中間層,進行客戶端和服務器端之間的協商,會產生304的狀況,經過last-modified和Etag進行相關判斷

若是不須要每次請求都從新驗證,就使用max-age和s-maxage。是否容許被代理服務緩存,若是no就是Private,只瀏覽器層面的緩存,咱們就設置爲max-age;若是是Public,就是能被不少人共同使用,那它就是一個源代理服務器,或者是cdn服務器,這時候就須要設置s-maxage。最後就是設置過時時間

2七、服務端優化(前提是服務端使用了node.js,這樣先後端使用的語言就同樣了,可使用服務端的運算能力來進行相關的運算,從而減小前端的運算成本)

     Vue渲染面臨的問題是什麼?

   Vue的項目,在頁面被渲染出來前,在客戶端已經作了大量的運算,Vue的dom結構是在組件的template裏面寫的,這就面臨一個問題,若是我整個頁面要渲染出來,其實是依賴於Vue框架加載的,若是整個網站都是用Vue寫的,那執行流程就是:先下載vue.js,而後執行Vue.js代碼,生成相應的html頁面,最後經過Vue框架把這個HTML渲染到頁面裏。所以,首屏顯示就依賴於整個Vue加載和執行,這樣會有一個性能上的損耗

使用React和Vue是有代價的,代價就是須要把框架加載完,才能把頁面渲染出來

這是就有個想法:咱們能不能想jsp/php同樣,在後端就渲染好,而後在前端直接展現

Vue的優勢:開發體驗好,組件庫利於維護

怎麼在vue這個層面對性能進行優化?

構建層模板編譯:就是把vue代碼使用webpack等工具,編譯成能夠run time的代碼

數據無關的prerender的方式:把一些與數據無關的,全部用戶都看見同樣的頁面,prerender掉,生成靜態html,而後使用這個靜態的html去訪問相應的vue頁面

服務端渲染(主要是爲了解決首屏渲染問題):把一些常常改變的頁面,在服務端渲染。在服務端執行相應的js,生成相應的html,而後在客戶端直接進行渲染,把一些客戶端須要執行的運算,在服務端完成

相關文章
相關標籤/搜索