前言
當我們去面試的時候,很大機率會被面試官問這麼一個問題:你有嘗試過對專案做效能最佳化嗎?或者你瞭解哪些效能最佳化的方法?聽到這個問題的你可能是這樣的:
似曾相識但又說不清楚,往往只能零散地說出那麼幾點,難以做到有條理的回答。那麼,本文就帶你簡單瞭解前端效能最佳化的幾個主要方面,旨在拋磚引玉。
一、資源的合併和壓縮
請求過程中一些潛在的效能最佳化點:
dns
是否可以透過快取減少dns
查詢時間?- 網路請求的過程如何走最近的網路環境?
- 相同的靜態資源是否可以快取?
- 能否減少
http
請求的大小和次數? - 能否進行服務端渲染?
總結: 深入理解http
請求的過程是前端效能最佳化的核心。
最佳化核心
- 減少
http
請求數量; - 減少請求資源的大小;
google
首頁案例學習
html
壓縮;css
壓縮;js
的壓縮和混亂;- 檔案合併;
- 開啟
gzip
;
1.html
壓縮
HTML
程式碼壓縮就是壓縮一些在文字檔案中有意義,但是在HTML
中不顯示的字元,包括空格,製表符,換行符等,還有一些其他意義的字元,如HTML
註釋也可以被壓縮;
一個簡單的計算:
google
的流量,佔到整個網際網路的40%
,預計2016
年全球網路流量將達到1.3ZB(1ZB = 10^9TB)
,那麼google
在2016
年的流量就是1.3ZB * 40%
,如果google
每1MB
請求減少一個位元組,每年可以節省流量近500TB
流量。
如何進行html
壓縮
- 使用線上網站進行壓縮;
nodejs
提供的html-minifier
工具;- 後端模板引擎渲染壓縮;
2.css
程式碼壓縮
分為兩部分:
- 無效程式碼的壓縮;
css
語義合併;
如何進行css
壓縮
- 使用線上網站進行壓縮;
- 使用
html-minifier
對html
中的css
進行壓縮; - 使用
clean-css
對css
進行壓縮;
3.js
壓縮與混亂(醜化)
包括:
- 無效字元的刪除(空格,回車等);
- 剔除註釋;
- 程式碼語義的縮減和最佳化;
- 程式碼保護(如果程式碼不經處理,客戶端可直接窺探程式碼漏洞);
JS
壓縮與混亂(醜化)
- 使用線上網站進行壓縮
- 使用
html-minifier
對html
中的js
進行壓縮; - 使用
uglify.js2
對js
進行壓縮;
4.檔案合併
檔案合併的好處:
左邊的表示使用http
長連結keep-alive
但不合並請求的情況,需要分三次去獲取a.js
、b.js
、c.js
;右邊是使用長連結並且合併請求的情況,只需要傳送一次獲取合併檔案a-b-c.js
的請求,就能將三個檔案都請求回來。
不合並請求有下列缺點:
- 檔案與檔案之間有插入的上行請求,會增加
N-1
個網路延遲; - 受丟包問題的影響更嚴重:因為每次請求都可能出現丟包的情況,減少請求能有效減少丟包情況;
keep-alive
本身也存在問題:經過代理伺服器時可能會被斷開;
檔案合併存在的問題
- 首屏渲染問題:當請求
js
檔案的時候,如果頁面渲染只依賴a.js
檔案,由於檔案合併,需要等待合併後的a-b-c.js
檔案請求回來才能繼續渲染,這樣就會導致頁面渲染速度變慢。這種情況大多出現在現代化的前端框架,如Vue
等的使用過程中; - 快取失效問題:合併後的檔案
a-b-c.js
中只要其中一個檔案(比如a.js
)發生變化,那麼整個合併檔案都將失效,而不採用檔案合併就不會出現這種情況;
使用建議
- 公共庫合併:將不經常發生變化的公共元件庫檔案進行合併;
- 將不同頁面的
js
檔案單獨合併:比如在單頁面應用SPA
中,當路由跳轉到具體的頁面時才請求該頁面需要的js
檔案;
如何進行檔案合併
- 使用線上網站進行檔案合併;
- 使用
nodejs
實現檔案合併; - 使用
webpack
等前端構件化工具也可以很好地實現;
二、圖片相關的最佳化
有失真壓縮過程: 一張JPG
圖片的解析分別要進行:
- 顏色空間的轉換:從
RGB
的顏色空間轉到其他的顏色空間 ; - 進行重取樣:區分高頻和低頻的顏色變換;
- 進行
DCT
過程:對高頻的顏色取樣結果進行壓縮,這樣壓縮的收益會比較大; - 再對資料進行量化;
- 最後進行編碼(
encoding
);
最終得到JPEG-Compressed Image Data
,即真正顯示出來的JPG
圖片。雖然這是一種有失真壓縮,但是很多情況下,這些損失的資料並不影響顯示;
png8/png24/png32
之間的區別
png8
:256
色+
支援透明;png24
:2^24
色+
不支援透明;png32
:2^32
色+
支援透明;
參考 前端進階面試題詳細解答
不同格式圖片常用的業務場景
jpg
有失真壓縮,壓縮率高,支援透明;應用:大部分不需要透明圖片的業務場景;png
支援透明,瀏覽器相容好;應用:大部分需要透明圖片的業務場景;webp
(2010
年由谷歌推出)壓縮程度更好,在ios webview
中有相容性問題;應用:安卓全部;svg
向量圖,程式碼內嵌,相對較小,用於圖片樣式相對簡單的場景;應用:比如logo
和iconfont
;
1.圖片壓縮
針對真實圖片情況,捨棄一些相對無關緊要的色彩資訊,對圖片進行壓縮;
2.css
雪碧圖
將網站上用到的一些圖片整合到一張單獨的圖片中,從而減少網站HTTP
請求數量。原理為:設定整張雪碧圖可示區域,將想要顯示的圖示定位到該處(左上角);缺點:整合圖片比較大時,一次載入比較慢。
如天貓的雪碧圖:
很多情況下,並不是所有的小圖示都放在一張雪碧圖中,而是會適當進行拆分。現在使用雪碧圖的場景比較少了。
自動生成雪碧圖樣式
3.網頁內聯圖片(Image inline
)
將圖片的內容內嵌到html
當中,減少網站的HTTP
請求數量,常用於處理小圖示和背景圖片。網頁內聯圖片寫法為:
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA..." alt="">
缺點:
- 瀏覽器不會快取內聯圖片資源;
- 相容性較差,只支援
ie8
以上瀏覽器; - 超過
1000kb
的圖片,base64
編碼會使圖片大小增大,導致網頁整體下載速度減慢;
所以要根據場景使用,不過內聯圖片減少HTTP
請求的優點還是很顯著的。比如,在開發中小於4KB
或8KB
的圖片都會透過構建工具自動inline
到HTML
中,這種情況下Image inline
帶來的圖片大小增長其實是比增加HTTP
請求次數更優的。
4.向量圖SVG
與iconfont
使用iconfont
解決icon
問題
應儘量使用該方式,比如可以採用阿里巴巴向量相簿:
可以選擇格式進行下載:
可以看到它們的大小有著明顯的差異:
使用SVG
進行向量圖的控制
SVG
意為可縮放向量圖形(Scalable Vector Graphics
)。SVG
使用 XML
格式定義影像。
5.webp
webp
的優勢體現在它具有更優的影像壓縮演算法,能帶來更小的圖片體積,而且擁有肉眼識別無差異的影像質量;同時具備了無損和有損的壓縮模式、Alpha
透明以及動畫的特性。在JPEG
和PNG
上的轉化效果都非常優秀、穩定和統一。安卓上不存在相容性問題,推薦安卓下使用。
以下為淘寶網首頁請求的圖片:
可以看到,圖片中大量地新增了webp
格式的選擇。.jpg_.webp
表示當瀏覽器支援webp
時採用webp
格式,否則採用jpg
格式。
下面為B
站首頁的圖片,可以看到基本都採用了webp
格式:
同一張圖片jpg
格式和webp
格式壓縮率有著明顯的差異:
可以透過線上網站將圖片轉換為webp
像圖片這樣的靜態檔案可以存放在CDN
伺服器上,讓CDN
伺服器批次地將圖片轉換成Webp
格式;
三、瀏覽器渲染引擎與阻塞
1.渲染的主要模組
版本一:
版本二:
一個渲染引擎主要包括:HTML
解析器,CSS
解析器,javascript
引擎,佈局layout
模組,繪圖模組:
HTML
解析器:解釋HTML
文件的解析器,主要作用是將HTML
文字解釋成DOM
樹;CSS
解析器:它的作用是為DOM
中的各個元素物件計算出樣式資訊,為佈局提供基礎設施;Javascript
引擎:使用Javascript
程式碼可以修改網頁的內容,也能修改css
的資訊,javascript
引擎能夠解釋javascript
程式碼,並透過DOM
介面和CSS
樹介面來修改網頁內容和樣式資訊,從而改變渲染的結果;- 佈局(
layout
):在DOM
建立之後,Webkit
需要將其中的元素物件同樣式資訊結合起來,計算他們的大小位置等佈局資訊,形成一個能表達這所有資訊的內部表示模型; - 繪圖模組(
paint
):使用圖形庫將佈局計算後的各個網頁的節點繪製成影像結果;
2.渲染過程
瀏覽器渲染頁面的整個過程:瀏覽器會從上到下解析文件。
瀏覽器解析時遇見
HTML
標記,就會呼叫HTML
解析器解析為對應的token
(一個token
就是一個標籤文字的序列化)並構建DOM
樹(就是一塊記憶體,儲存著tokens
,建立它們之間的關係)。在生成DOM
的最開始階段(應該是Bytes
→characters
後),並行發起css
、圖片、js
的請求,無論他們是否在HEAD
標籤中。注意:發起
js
檔案的下載請求(request
)並不需要DOM
處理到那個script
節點;- 遇見
style/link
標記 呼叫解析器 處理CSS
標記並構建CSS
樣式樹; - 遇見
script
標記 呼叫javascript
解析器處理script
標記,繫結事件、修改DOM
樹/CSS
樹等; - 將
DOM
樹 與CSS
樹 合併成一棵渲染樹(Render Tree
)。 - 佈局(
Layout
):根據渲染樹中各節點的樣式和依賴關係,計算出每個節點在螢幕中的位置; - 繪圖(
Painting
):按照計算出來的結果:要顯示的節點、節點的CSS
與位置資訊,透過顯示卡,把內容畫到螢幕上;
經過第一次Painting
之後DOM
、CSSOM
、Render Tree
都可能會被多次更新,比如JS
修改了DOM
或者CSS
屬性時,Layout
和Painting
就會被重複執行。除了DOM
、CSSOM
更新的原因外,圖片下載完成後也需要呼叫Layout
和 Painting
來更新網頁。
補充:
HTML
中可能會引入很多的css、js
這樣的外部資源,這些外部資源在瀏覽器端是併發載入的。但是瀏覽器會對同一域名進行併發數量(度)的限制,即單個域名的併發度是有限的;- 所以,經常將大部分的資源託管到
CDN
伺服器上,並且設定3~4
個CDN
域名。防止只有一個CDN
域名的情況下,達到了瀏覽器外部資源併發請求數目的上限,導致很多資源無法做到併發請求。所以,應設定多個CDN
域名;
3.css
阻塞
只有透過link
引入的外部css
才會產生阻塞:
style
標籤中的樣式:- 由
html
解析器進行解析; - 不阻塞瀏覽器渲染(可能會產生“閃屏現象”);
- 不阻塞
DOM
解析;
- 由
link
引入的外部css
樣式(推薦使用的方式):- 由
CSS
解析器進行解析; - 阻塞瀏覽器渲染:由於
css
已經載入完畢,所以整個渲染過程是帶樣式的,所以這種阻塞可以避免“閃屏現象”; - 阻塞其後面的
js
語句的執行:這個不難理解,js
檔案中經常會出現DOM
操作,操作過程中有可能涉及到css
樣式的修改。實際上,這些修改往往是依賴於之前引入的css
設定的樣式的,所以css
會阻塞js
的執行; - 不阻塞DOM的解析;
- 由
最佳化核心理念:儘可能快的提高外部
css
載入速度:- 使用
CDN
節點進行外部資源加速; - 對
css
進行壓縮(利用打包工具,比如webpack
,gulp
等); - 減少
http
請求數,將多個css
檔案合併; - 最佳化樣式表的程式碼;
- 使用
4.js
阻塞
阻塞DOM解析:
原因:瀏覽器不知道後續指令碼的內容,如果先去解析了下面的
DOM
,而隨後的js
刪除了後面所有的DOM
,那麼瀏覽器就做了無用功,瀏覽器無法預估指令碼里面具體做了什麼操作,例如像document.write
這種操作,索性全部停住,等指令碼執行完了,瀏覽器再繼續向下解析DOM
;可以透過給script
標籤新增defer
和async
屬性,非同步引入js
檔案,以此來解決這一問題。阻塞頁面渲染:
原因:
js
中也可以給DOM
設定樣式,瀏覽器同樣等該指令碼執行完畢,再繼續幹活,避免做無用功;阻塞後續
js
的執行:原因:
js
是按順序執行的,這樣可以維護依賴關係,例如:必須先引入jQuery
再引入bootstrap
;不阻塞資源的載入:
這並不與上面矛盾,因為不可能由於載入一個
js
檔案就把其他資源的載入都阻塞了。針對這種常見的情況,瀏覽器會透過預載入的方式載入後續的資源;
5.總結
css
的解析和js
的執行是互斥的(互相排斥),css
解析的時候js
停止執行,js
執行的時候css
停止解析;無論
css
阻塞,還是js
阻塞,都不會阻塞瀏覽器載入外部資源(圖片、影片、樣式、指令碼等);因為覽器始終處於一種:“先把請求發出去”的工作模式,只要是涉及到網路請求的內容,無論是:圖片、樣式、指令碼,都會先傳送請求去獲取資源,至於資源到本地之後什麼時候用,由瀏覽器自己協調。顯然這種做法效率很高;
WebKit
和Firefox
都進行了【預解析】這項最佳化。在執行js
指令碼時,瀏覽器的其他執行緒會解析文件的其餘部分,找出並載入需要透過網路載入的其他資源。透過這種方式,資源可以在並行連線上載入,從而提高總體速度。請注意,預解析器不會修改DOM
樹
四、懶載入和預載入
1.懶載入
圖片進入可視區域之後再請求圖片資源的方式稱為圖片懶載入。適用於圖片很多,頁面很長的業務場景,比如電商;
懶載入的作用:
減少無效資源的載入:
比如一個網站有十頁圖片,使用者只檢視了第一頁的圖片,這就沒必要將十頁圖片全都載入出來;
併發載入的資源過多會阻塞
js
的載入,影響網站正常的使用:由於瀏覽器對某一個
host name
是有併發度上限的,如果圖片資源所在的CDN
和靜態資源所在的CDN
是同一個的話,過多圖片的併發載入就會阻塞後續js
檔案的併發載入。
懶載入實現的原理:
監聽onscroll
事件,判斷可視區域位置:
圖片的載入是依賴於src
路徑的,首先可以為所有懶載入的靜態資源新增自定義屬性欄位,用於儲存真實的url
。比如是圖片的話,可以定義data-src
屬性儲存真實的圖片地址,src
指向loading
的圖片或佔位符。然後當資源進入視口的時候,才將src
屬性值替換成data-src
中存放的真實url
。
<img src="" class="image-item" alt="" lazyload = "true" data-src="TB27YQvbm_I8KJjy0FoXXaFnVXa_!!400677031.jpg_180x180xzq90.jpg_.webp">
懶載入例項
可以使用元素的getBoundingRect().top
來判斷當前位置是否在視口內,也可以使用元素距離文件頂部的距離offsetTop
和scrollTop
是否小於視口高度來判斷:
舉例
比如手機淘寶首頁:
當快要滾動到需要展示的圖片時才進行圖片的請求,可以看到圖片上有一個lazyload
的屬性:
2.預載入
預載入與懶載入正好是相反的過程:懶載入實際上是延遲載入,將我們所需的靜態資源載入時間延後;而預載入是將圖片等靜態資源在使用之前的提前請求,這樣資源在使用到時能從快取中直接載入,從而提升使用者體驗;
預載入的作用:
- 提前請求資源,提升載入速度:使用時只需要讀取瀏覽器快取中提前請求到的資源即可;
維護頁面的依賴關係:比如
WebGL
頁面,會依賴一些3D
模型,這些都是頁面渲染所必須的資源。如果資源都沒有載入完畢就進行頁面的渲染,就會造成非常不好的體驗。所以時常使用預載入的方式維護頁面渲染的依賴關係,比如將
WebGL
頁面依賴的3D
模型載入完之後才進行頁面渲染。這樣渲染的過程就不會有任何阻礙,具有較好的使用者體驗;
預載入的例項
例如九宮格抽獎業務,每個獎品都有一個選中態和非選中態,實際上這是由兩張圖片組合而成的。由於每個獎品的選中過程都是一瞬間,這就對圖片的選中態和非選中態切換效率要求很高,如果選中態的圖片沒有預載入的話顯然是來不及的。
所以,實際上對於九宮格中所有圖片選中態的樣式和對應的圖片都需要進行預載入,從而讓我們在抽獎的過程中,能夠瞬間從快取中讀取到選中態的圖片,從而不影響抽獎效果的展示。
除此之外還有網站登入或活動時需要用到的動畫,這是在動畫需要的每幀圖片都完全預載入完之後才會進行顯示的。
五、重繪與迴流
1.CSS
圖層
瀏覽器在渲染一個頁面時,會將頁面分為很多個圖層,圖層有大有小,每個圖層上有一個或多個節點。在渲染 DOM
的時候,瀏覽器所做的工作實際上是:
1、獲取`DOM`後分割為多個圖層;
2、對每個圖層的節點計算樣式結果(`Recalculate style`--樣式重計算);
3、為每個節點生成圖形和位置(`Layout`--迴流和重佈局);
4、將每個節點繪製填充到圖層點陣圖中(`Paint Setup`和`Paint`--重繪);
5、圖層作為紋理上傳至`GUI`;
6、複合多個圖層到頁面上生成最終螢幕影像(`Composive Layers`--圖層重組);
2.建立圖層的條件
- 擁有
3D
或透視變換的css
屬性(prespective transform
); - 使用加速影片解碼的
<video>
節點; - 擁有
3D(WebGL)
上下文或加速的2D
上下文的<canvas>
節點; CSS3
動畫的外掛(如Flash
);擁有加速
css
過濾器的元素;transform
:如translateZ(0)
opacity
filter
will-change
:哪一個屬性即將發生變化,進而進行最佳化。
3.重繪(Repaint)
重繪是一個元素外觀的改變所觸發的瀏覽器行為,比如background-color
、outline
等屬性。這些屬性不影響佈局,隻影響元素的外觀,風格,會造成DOM
元素的重新渲染,這個過程稱為重繪。
需要注意的是:重繪是以圖層為單位,如果圖層中某個元素需要重繪,那麼整個圖層都需要重繪。比如一個圖層包含很多節點,其中有個gif
圖,gif
圖的每一幀,都會重回整個圖層的其他節點,然後生成最終的圖層點陣圖。
因此,可以透過特殊的方式來強制gif
圖單獨為一個圖層(translateZ(0)
或者translate3d(0,0,0)
;CSS3
的動畫也是一樣(好在絕大部分情況瀏覽器自己會為CSS3
動畫的節點建立圖層);
所以:將頻繁重繪迴流的DOM
元素作為一個獨立圖層,那麼這個DOM
元素的重繪和迴流只會該圖層;原則上是要儘量避免新建圖層的,因為這會導致圖層重組(Composive Layers
)時候的計算量增大。所以,只有當某些DOM
元素頻繁重繪迴流時,才新建一個獨立圖層放置它們;
只會觸發重繪的屬性
//部分屬性
color
border-style
border-radius
visibility
text-decoration
background
background-image
background-position
background-repeat
background-size
outline-color
outline
outline-style
outline-width
box-shadow
4.迴流(Reflow)
當render tree
中的一部分(或全部)因為元素的規模尺寸,佈局,隱藏等改變而需要重新構建。這就稱為迴流(reflow
);
- 當頁面佈局和幾何屬性改變時就需要回流;
- 迴流必將引起重繪,而重繪不一定會引起迴流;
觸發頁面重佈局(迴流)的屬性
盒子模型相關屬性 | 定位及浮動屬性 | 文字結構屬性 |
---|---|---|
width | top | text-align |
height | bottom | overflow-y |
padding | left | font-weight |
margin | right | overflow |
display | position | font-family |
border-width | float | line-height |
border | clear | vertical-align |
min-height | * | white-space |
* | * | font-size |
頻繁觸發重繪和迴流,會導致UI
頻繁渲染。在渲染的過程中由於阻塞了js
執行緒的執行,最終導致js
執行變慢。
5.觸發迴流的常見操作
- 增加、刪除、修改
DOM
結點; - 移動
DOM
的位置; - 修改
CSS
樣式; Resize
視窗;移動端沒有這個問題,因為移動端的縮放沒有影響佈局視口(vw/vh
);- 修改網頁的預設字型;
- 獲取某些
DOM
元素的屬性(width
,height
等);
注:display:none
會觸發Reflow
,而visibility:hidden
只會觸發Repaint
,因為沒有發生位置變化;
6.示例
案例一:淘寶輪播圖
可以使用Chrome
瀏覽器除錯工具的Performance
來觀察淘寶首頁一個輪播圖引起的重繪迴流過程:
Update Layer Tree
迴流和重佈局:
Paint
重繪:
Composite Layers
圖層重組:
案例二:播放器
透過Chrome
除錯工具的Layers
選項檢視圖層,及新增圖層的原因:
影片播放的過程中,video
標籤的DOM
元素會一直重繪,所以把它限制在一個圖層上是非常好的,這樣只會涉及到這個圖層的重繪,而不會影響其他圖層的元素。
圖層不能濫用,否則會在圖層重組的過程中嚴重消耗效能!
比如可以將淘寶首頁的所有的DOM
元素都變為一個圖層:在html
標籤中的全域性樣式(*
)中新增transform:translateZ(0)
來觸發新建圖層:
還可以透過新增:will-change: transform
屬性新建圖層;
再次檢視此時的圖層情況,可以看到此時首頁的圖層非常之多,十分地卡:
7.實戰最佳化點
如果我們需要使得動畫或其他節點渲染的效能提高,需要做的就是減少瀏覽器在執行時所需要做的下列工作:
- 計算需要被載入到節點上的樣式結果(
Recalculate style
--樣式重計算); - 為每個節點生成圖形和位置(
Layout
--迴流和重佈局); - 將每個節點填充到圖層中(
Paint Setup
和Paint
--重繪); - 組合圖層到頁面上(
Composite Layers
--圖層重組);
1、使用translate
替代top
等屬性來改變位置;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#box{ /*方法1*/
position: relative; top: 0; /*方法2*/
/* transform: translateY(0); */
width: 200px; height: 200px; background-color: pink; } </style>
</head>
<body>
<div id="box"></div>
<script>
setTimeout(() => { document.getElementById("box").style.top = '100px'
//document.getElementById("box").style.transform = 'translateY(100px)'
}, 2000); </script>
</body>
</html>
使用top
屬性改變正方形位置時,存在重繪和迴流Layout
:
而使用translate
屬性改變正方形位置時,並不會引起重繪和迴流:
比如有的網站會有一些左右飄動的浮窗,由於這些浮窗是採用定時器來實現的,如果每經過100ms
就改變浮窗的位置。這種時候使用transform
來替代top/left
的話1s
內就減少了十次迴流,十分有利於網頁速度的提升。
2、使用opacity
替代visibility
:
- 使用
visibility
不觸發重排,但是依然重繪; - 直接使用
opacity
既觸發重繪,又觸發重排(GPU
底層設計如此!); opacity
配合圖層使用,既不觸發重繪也不觸發重排;原因:透明度的改變時,
GPU
在繪畫時只是簡單的降低之前已經畫好的紋理的alpha
值來達到效果,並不需要整體的重繪。不過這個前提是這個被修改opacity
本身必須是一個單獨的圖層。
3、將多次改變DOM
元素樣式屬性的操作合併成一次操作:
- 預先定義好
class
,然後透過修改DOM
的className
來新增樣式;
4、把DOM
離線後再修改:
- 由於
display
屬性為none
的元素不在渲染樹中,對隱藏的元素操作不會引發其他元素的重排。如果要對一個元素進行復雜的操作時,可以先隱藏它,操作完成後再顯示。這樣只在隱藏和顯示時觸發2
次迴流;
5、不要把獲取某些DOM
節點的屬性值放在一個迴圈裡當成迴圈的變數
當向瀏覽器請求某些 style
資訊的時候,瀏覽器就會清空(flush
)佇列,比如:
ffsetTop
,offsetLeft
,offsetWidth
,offsetHeight
;scrollTop/Left/Width/Height
;clientTop/Left/Width/Height
;width
,height
;
瀏覽器為了獲取最精確的值,需要重新整理內部佇列。因為佇列中可能存在影響到這些值的操作,即使沒有,瀏覽器也會強行重新整理渲染佇列。這樣就無法利用渲染佇列的快取來避免迴流過於頻繁了,所以在使用到DOM
元素這些相關的屬性時,可以將獲取到的屬性值存在一個變數中,而不是每次都去重新獲取。
6、不要使用table
佈局:
- 因為很小的一個改動都會造成整個
table
的重新佈局;所以儘量使用div
佈局;
7、啟用GPU
硬體加速:
原理為:瀏覽器會檢測一些特定的css
屬性,當DOM
元素擁有這些css
屬性的時候,瀏覽器就會對該DOM
元素啟動GPU
硬體加速;比如:transform: translateZ(0)
和transform: translate3d(0, 0, 0)
這兩個屬性都可以啟動硬體加速;硬體加速同樣不能濫用,否則會導致圖層過多,導致合併圖層時消耗大量效能。
8、動畫實現速度的選擇:
- 因為動畫的每次變化都會引起重繪和迴流,所以要根據業務場景適當地在動畫幀數(順暢程度)和迴流次數中進行平衡;
9、為動畫元素新建圖層,提高動畫元素的z-index
;
10、利用文件碎片(documentFragment
)------vue
使用了該種方式提升效能
如果我們要在一個ul
中新增10000
個li
,如果不使用文件碎片,那麼我們就需要使用append
進行10000
次的追加,這會導致頁面不停地迴流,非常地消耗資源:
var oUl = document.createElement("ul");
for(var i=0;i<10000;i++)
{
var oLi = document.createElement("li");
oUl.appendChild(oLi);
}
document.body.appendChild(oUl);
我們可以引入createDocumentFragment()
方法,它的作用是建立一個文件碎片。先將要插入10000
個li
新增到文件碎片裡,然後再一次性新增到document
中。即文件碎片相當於一個臨時倉庫,這樣能夠大量減少DOM
操作:
//先建立文件碎片
var oFragment = document.createDocumentFragment();
//再建立ul標籤
var oUl = document.createElement("ul");
for(var i=0;i<10000;i++)
{
//建立li標籤
var oLi = document.createElement("li");
//先附加在文件碎片中
oFragment.appendChild(oLi);
}
//將文件碎片新增到ul標籤中
oUl.appendChild(oFragment);
//將ul標籤新增到body標籤中
document.body.appendChild(oUl);
11、如果涉及到一些可以使用合成執行緒來處理 CSS
特效或者動畫的情況,就儘量使用 will-change
來提前告訴渲染引擎,讓它為該元素準備獨立的層。
12、採用虛擬DOM
;
13、使用requestAnimationFrame
製作動畫:詳細內容如下。
8.請求動畫幀(requestAnimationFrame
)
window.requestAnimationFrame()
:該方法會告訴瀏覽器在重繪之前呼叫指定的函式:
引數:該方法以一個回撥函式作為引數,這個回撥函式會在瀏覽器重繪之前被呼叫;
回撥函式會被自動傳入一個引數:
DOMHighResTimeStamp
,標識requestAnimationFrame()
開始觸發回撥函式的當前時間;- 返回值: 一個非零的整數,也稱為請求
ID
,是回撥列表中唯一的標識,沒有其他意義;
window.cancelAnimationFrame(requestID)
:該方法取消一個先前透過呼叫window.requestAnimationFrame()
方法新增到計劃中的動畫幀請求。requestID
是先前呼叫window.requestAnimationFrame()
方法時返回的ID
。
用途
- 當無法使用
CSS3
製作動畫的情況下,使用這種方法替代定時器製作動畫; - 由於重繪就呼叫的機制,製作的動畫頻率與瀏覽器的重新整理頻率一致,不會出現閃動,保證了動畫的流暢;
示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#box{ height: 200px; width: 200px; background-color: pink; } </style>
</head>
<body>
<div id="box"></div>
<script>
let i = 0
//獲取請求ID
let id = requestAnimationFrame(move) function move(){ i++ document.getElementById('box').style.transform = `translateX(${i}px)`
//遞迴呼叫requestAnimationFrame,更新請求ID,實現動畫效果
id = requestAnimationFrame(move) } setTimeout(() => { //2s後停止動畫
cancelAnimationFrame(id) }, 2000); </script>
</body>
</html>
六、函式防抖與節流
1.函式防抖
- 概念:不斷觸發一個函式,在規定時間內只讓最後一次生效,前面都不生效;
- 實現:定時器;
- 應用:搜尋時等使用者完整輸入內容後再傳送查詢請求;
程式碼實現
function debounce(fn,delay){
var timer = null
// 清除上一次延時器
return function(){
clearTimeout(timer)
// 重新設定一個新的延時器
timer = setTimeout(() => {
fn.call(this)
}, delay);
}
}
使用函式防抖可以減少事件觸發的次數和頻率,在某些情況下可以起到最佳化的作用。比如:搜尋框,對於核心業務非搜尋的網站,一般都是等待使用者完整輸入內容後才傳送查詢請求,一次來減少伺服器的壓力。像百度這樣的核心業務為搜尋的網站,伺服器效能足夠強大,所以不進行函式防抖處理;
2.函式節流
- 概念:不斷觸發一個函式後,執行第一次,只有大於設定的執行週期後才會執行第二次,以此控制函式執行頻率;
- 實現:定時器,標識;
- 應用:在遊戲中,可以設定人物攻擊動作的最快頻率,無論手速多快也無法超越這一頻率;
程式碼實現
*
節流函式:fn:要被節流的函式,delay:規定的時間
*/
function throttle(fn, delay){
// 記錄上一次函式出發的時間
var lastTime = 0
return function(){
// 記錄當前函式觸發的時間
var nowTime = new Date().getTime()
// 噹噹前時間減去上一次執行時間大於這個指定間隔時間才讓他觸發這個函式
if(nowTime - lastTime > delay){
// 繫結this指向
fn.call(this)
//同步時間
lastTime = nowTime
}
}
}
七、瀏覽器儲存
1.Cookie
Cookie
翻譯過來是小甜餅的意思,是網景公司的前僱員Lou Montulli
在1993
年3
月發明的;Cookie
是純文字格式,不包含任何可執行的程式碼資訊,伴隨著使用者請求在Web
伺服器和瀏覽器之間傳遞;Cookie
本質上屬於http
的範疇,因為http
協議本身是無狀態的,服務端是沒有辦法區分請求來自於哪個客戶端,即便是來自於同一個客戶端的多次請求,服務端也無法進行區分。所以引入了Cookie
去維持客戶端的狀態(比如每個賬號的購物車狀態都不一樣)。
Cookie
的生成方式
客戶端生成:
在
JavaScript
中透過document.cookie
屬性,你可以建立、維護和刪除Cookie
;設定document.cookie
屬性的值並不會刪除儲存在頁面中的所有Cookie
,它只簡單的建立或修改字串中指定的Cookie
。服務端生成:
Web
伺服器透過在HTTP
響應頭中新增Set-Cookie
欄位來建立一個Cookie
,可以在該欄位中新增HttpOnly
屬性禁止JavaScript
指令碼訪問Cookie
,以此來避免跨域指令碼 (XSS
) 攻擊。
Cookie
的缺陷
- 安全性:由於
Cookie
在HTTP
中是明文傳遞的,其中包含的資料都可以被他人訪問,出現篡改、盜用等問題; - 大小限制:
Cookie
的大小限制在4KB
左右,若要做大量儲存顯然不是理想的選擇; 增加流量:因為
Cookie
是繫結域名對應的伺服器的,所以對同一個域名的每次請求都會在Request Header
中帶上Cookie
。- 一方面:增加對伺服器的請求時間;
- 另一方面:導致大部分不需要用到
Cookie
資訊的場合下流量的浪費;這樣瀏覽器對同一域名的每一次請求都會多出4KB
流量,對於大型網站來說這是很大的損耗。
因此要慎用Cookie
,不要在Cookie
中儲存重要和敏感的資料。
Cookie
效能最佳化的方法
將存放靜態資源的CDN
伺服器域名與主站的域名獨立開來。這樣每次請求靜態檔案的時候就不需要攜帶Cookie
,從而可以節省很多流量。
舉例
比如在百度進行登入的時候,請求頭裡面就會有Set-Cookie
欄位,其中的BDUSS
就是標識使用者登入狀態的字串:
Set-Cookie
中的httponly
屬性表示的是禁止js
指令碼訪問cookie
,這樣能夠一定程度防範XSS
攻擊;
在Chrome
除錯工具的Application
選項中檢視Cookies
資訊,可以發現該Cookie
已經被網站“種”到Domain:.baidu.com
這個域名下了,並且該Cookie
也設定了HttpOnly
屬性:
此後瀏覽器的每次請求都會在請求頭Request Headers
中攜帶這一Cookie
資訊。重新整理頁面後可以看到,請求頭中攜帶了Cookie
資訊BDUSS
:
這樣伺服器就知道這是已經登入的使用者了。
但是不是所有的請求都需要攜帶Cookie
資訊,比如優酷:
可以看到請求index.css
檔案時也攜帶了Cookie
,但是這是不必要的,這就會導致流量的浪費。
解決方法就是上面所說的:將CDN
域名和主域名獨立出來;
百度就是這樣解決的:
可以看到請求這個靜態資源的url
並不是.baidu.com
,而是靜態資源伺服器CDN
;並且該請求的請求頭中不會攜帶Cookie
資訊:
設定和獲取Cookie
設定Cookie
的方式很簡單,key
和value
值透過等號連線:
document.cookie = "userName=zhangsan"
開啟Application
選項檢視當前Cookie
,可以看到Cookie
已被改變:
獲取Cookie
:
document.cookie
備註:
- 靜態資源是不會攜帶
Cookie
的;Cookie
一般都是後臺種的,很少讓前端來直接寫;Cookie
分:持久級別、session
級別;Cookie
一般用於存放session ID
與伺服器端進行通訊;
2.Web Storage
Web Storage
分為SessionStorage
和LocalStorage
專門用於客戶端瀏覽器的本地儲存,同時空間比Cookie
大很多,一般支援5-10M
;- 瀏覽器端透過
Window.sessionStorage
和Window.localStorage
屬性來實現本地儲存機制;
LocalStorage
LocalStorage
是HTML5
設計出來專門用於儲存瀏覽器資訊的:
- 大小為
5~10M
左右; - 僅在客戶端中使用,不和服務端進行通訊;
- 介面封裝較好,提供了
js
進行讀寫等操作的API
; - 採用瀏覽器本地快取方案,可直接使用瀏覽器本地快取,提升網頁渲染的速度;
舉例
比如透過Chrome
除錯工具的Application
選項可以檢視淘寶中LocalStorage
儲存的資料:
這些資料只要不手動清除,即使關閉頁面也都會存在。當需要使用圖片、js/css
檔案等資源時就不用重新向伺服器發出請求,而是可以直接使用LocalStorage
中的快取,這就是LocalStorage
快取的優勢;
而Cookie
就不一樣了,裡面儲存的資料都是要帶到伺服器端的,例如使用者登入狀態,統計資訊等資料:
設定和獲取LocalStorage
LocalStorage
提供了相對簡單的API
,採用的也是key
和value
的形式。
設定時透過:
localStorage.setItem("key", "value")
檢視LocalStorage
,同樣設定成功了:
獲取時透過:
localStorage.getItem("key")
其他方法
//該方法接受一個鍵名作為引數,並把該鍵名從儲存中刪除。
localStorage.removeItem('key');
//呼叫該方法會清空儲存中的所有鍵名
localStorage.clear();
SessionStorage
SessionStorage
用於儲存瀏覽器的會話資訊,標籤頁關閉之後它儲存的資料就會被清空,而LocalStorage
的資料不會被清空,這是二者的區別:
- 大小為
5~10M
左右; - 僅在客戶端使用,不和服務端進行通訊;
- 介面封裝較好;
- 可對錶單資訊進行維護;比如新增表單過程中進行了重新整理,可以將重新整理前填寫的資訊寫入
SessionStorage
中,這樣即使重新整理後資料也不會丟失;還有一種場景:分頁的表單在進行前進或後退時,如果將資訊儲存在SessionStorage
中就不會丟失;
設定和獲取SessionStorage
設定SessionStorage
的方法與設定LocalStorage
的方法類似:
//設定
sessionStorage.setItem("key", "value")
//獲取
sessionStorage.getItem("key")
透過Application
選項檢視SessionStorage
,可見已成功修改: 其他方法
//該方法接受一個鍵名作為引數,並把該鍵名從儲存中刪除。
sessionStorage.removeItem('key');
//呼叫該方法會清空儲存中的所有鍵名
sessionStorage.clear();
3.IndexedDB
IndexedDB
是瀏覽器提供的一種API
,用於儲存客戶端中大量的結構化資料。該API
使用索引來實現對資料的高效能搜尋。雖然WebStorage
對於儲存較少量的資料時很有用(採用key/value
的方式),但對於儲存更大量的結構化資料來說,還是IndexedDB
表現更加優異。
IndexedDB
的應用
- 為應用建立離線版本;
可以在瀏覽器中列印indexedDB
物件:
4.PWA
PWA
(Progressive Web Apps
)是一種Web App
新模型(標準),並不是具體指某一種前沿的技術或者某一個單一的知識點。從英文縮寫就能看出,這是一個漸進式的Web App
,是透過一系列新的Web
特性,配合優秀的UI
互動設計,逐步增強使用者的體驗;
PWA
的要求
- 可靠:在沒有網路的環境中也能提供基本的頁面訪問,而不會出現"未連線到網際網路"的情況;
- 快速:針對網頁渲染及網路資料訪問有較好最佳化;
- 融入(
Engaging
):應用可以被增加到手機桌面,並且和普通應用一樣有全屏、推送等特性;
5.Service Worker
Service Worker
是一個指令碼,可以使瀏覽器獨立於當前網頁,在後臺執行。為實現一些不依賴頁面或者使用者互動的特性開啟了一扇大門。在未來這些特性將包括推送資訊,背景後臺同步,geofencing
(地理圍欄定位)等它將推出的第一個首要特性,就是攔截和處理網路請求的能力,包括以程式設計方式來管理被快取的響應。
即Service Worker
可以幫助瀏覽器執行大規模的運算而不阻礙主執行緒的執行。
Service Worker
的應用
- 使用攔截和處理網路請求的能力,實現一個離線應用;
- 使用
Service Worker
在後臺執行的同時能和頁面通訊的能力,去實現大規模後臺資料的處理;
Service Worker
應用過程
示例
透過Chrome
除錯工具的Application
選項可以檢視淘寶的Service Workers
資訊:
當我們重新整理淘寶網頁的時候,檢視Network
選項,可以從請求檔案的size
欄發現大量的檔案都是從Service Worker
快取中請求回來的:
這樣的話就可以利用Service Worker
的快取進行網站的效能最佳化。
以下列淘寶請求同一js
檔案為例,從Service Worker
中載入使用了7ms
:
使用Ctrl + F5
強制重新整理後,向伺服器請求同一檔案花了100ms
:
這就是使用Service Worker
效能上帶來的優勢。由於是從本地快取中讀取的資源,所以資源讀取的速度和整體的效能都會有一個明顯的提升。
八、HTTP通用快取策略
1.快取的簡介
快取定義:
瀏覽器在本地磁碟上將使用者之前請求的資料儲存起來,當訪問者再次需要改資料的時候無需再次傳送請求,直接從瀏覽器本地獲取資料
快取的好處:
- 減少請求的個數;
- 節省頻寬,避免浪費不必要的網路資源;
- 減輕伺服器壓力;
- 提高瀏覽器網頁的載入速度,提高使用者體驗;
2.快取相關的header欄位
可以透過Chrome
瀏覽器除錯工具中的Network
選項檢視瀏覽器請求資源的情況:
注意不要勾選圖中方框內的選項,否則有些請求會被過濾;
Cache-Control
欄位
伺服器可透過httpheader
中的Cache-Control
欄位控制客戶端與伺服器端之間的快取策略,它的屬性值有:
max-age
該欄位指定了快取的最大有效時間,以下為淘寶的一張圖片:
在max-age
屬性指定的時間未到期前,客戶端不會向伺服器發起請求,而是從快取中直接讀取該圖片。上圖中可以看到瀏覽器直接從ServiceWorker
的快取中讀取了該圖片資源。
Expires
欄位同樣可以指定快取的有效期,不過這是HTTP1.0
中的欄位,優先順序比HTTP1.1
中的Cache-Control
欄位的max-age
屬性低;
s-maxage
快取裝置總體來說有兩種:瀏覽器(客戶端)和CDN
伺服器;
- 其中瀏覽器屬於
private
型別快取裝置,表示只有瀏覽器才可以對資源進行快取; CDN
伺服器屬於public
型別快取裝置,這種裝置可以對源伺服器上的資源進行快取。並且,這種快取對於任何使用者來說都是可以訪問的;
s-maxage
的優先順序在Expires
和max-age
三者之中是最高的,用於指定public
型別快取裝置(比如CDN
)上資源的有效期。如下圖所示,該資源設定了該欄位後,瀏覽器既不會使用瀏覽器快取,也不會向伺服器請求資源,而是向public
型別的快取裝置(如CDN
伺服器)請求資源:
private
伺服器端可以透過該屬性指定某一資源只能被瀏覽器(客戶端)快取,而不能被代理快取伺服器(CDN
)快取。
public
伺服器端可以透過該屬性指定某一資源,既可以被瀏覽器快取,也可以被代理快取伺服器快取;
no-cache
no-cache
屬性規定了瀏覽器要先向伺服器端傳送請求確認快取資源的新鮮度,才能決定是否使用快取;如下圖所示:
no-store
該屬性指定了瀏覽器無論快取資源是否過期直接跳過快取,重新向伺服器請求資源。no-store
屬性用的比較少。
Expires
欄位
這是http1.0
的規範;它的值為一個絕對時間的GMT
(格林威治標準時間)格式時間字串,如Mon, 10 Jun 2015 21:31:12 GMT
;
該欄位指定了瀏覽器快取資源的過期時間,在指定的時間到期前,瀏覽器可以直接從本地快取中讀取資料,而無需再次向伺服器發起請求,屬於強快取;相比於max-age
與s-maxage
優先順序最低,在這兩個屬性存在的情況下Expires
欄位會失效;
標識資源變化的欄位
Last-Modified/If-Modified-Since
二者是基於客戶端和服務端協商的快取機制,標識資源最後更新時間的欄位。last-modified
欄位位於response header
中,If-Modified-Since
欄位位於request header
中,二者配合著Cache-Control
欄位使用。
當伺服器上的資源發生改變時會同步更新last-modified
的欄位值,當Expires
欄位或max-age
屬性指定的時間到期後,客戶端會在請求頭中攜帶If-Modified-Since
欄位,與伺服器端資源的last-modified
欄位值進行比較:
- 情況一:如果二者相等說明資源自
last-modified
欄位指定的時間以後都沒有發生變化,此時伺服器返回狀態碼304
,屬於協商快取; - 情況二:如果二者不相等說明資源發生了更新,伺服器返回最新的資源和最新的
last-modified
欄位值,此時的狀態碼為200
;
舉例
下圖表示狀態碼為304
的響應:
- 請求頭中
If-Modified-Since
欄位的值為Mon, 23 Mar 2020 18:14:15 GMT
: - 響應頭中
Last-Modified
欄位的值為Mon, 23 Mar 2020 18:14:15 GMT
:
二者相等,說明資源沒有發生變化,所以伺服器返回狀態碼304
,屬於協商快取,瀏覽器繼續使用本地快取;
If-Modified-Since
欄位的值就是伺服器端上一次響應資源中的Last-Modified
欄位值;
Last-Modified
的缺點
- 一些檔案也許會週期性的更改,但是他的內容並不改變(僅僅改變的修改時間),這個時候我們並不希望客戶端認為這個檔案被修改了,而重新
GET
; - 某些檔案修改非常頻繁,比如在秒以下的時間內進行修改,(比方說
1s
內修改了N
次),If-Modified-Since
能檢查到的粒度是s
級的,這種修改無法判斷(比如淘寶每ms
都會更新資料); - 某些伺服器端不能獲取精確的修改時間;
所以有了
Etag/If-None-Match
Etag
欄位是HTTP1.1
中的標準,是一個唯一標識伺服器端資源的hash
值,該欄位存在於響應頭(reponse header
)中;與請求頭(request header
)中的If-None-Match
欄位及Cache-Control
欄位配合使用。
只要伺服器端的資源發生變化Etag
值就會改變,相比於Last-Modified
欄位優先順序更高且更有效;當Expires
值或者Cache-Control
欄位中的max-age
值到期時,客戶端會在請求頭中攜帶If-None-Match
欄位,該欄位值為伺服器端上一次響應資源中的Etag
值,並與伺服器端上最新資源的Etag
值進行比較:
- 情況一:如果兩個欄位值相等,說明資源未發生改變,伺服器端拒絕響應,返回狀態碼
304
,屬於協商快取; - 情況二:如果兩個欄位值不相等說明伺服器端上的資源發生了改變,伺服器在響應中返回最新的資源和
Etag
值,此時狀態碼為200
;
舉例
下圖表示狀態碼為304
的響應:
- 請求頭中
If-None-Match
欄位值為2da25d4039...
: - 響應頭中
Etag
欄位值為2da25d4039...
:
二者相等,說明資源沒有發生變化,所以伺服器返回狀態碼304
,屬於協商快取,瀏覽器繼續使用本地快取;
總結:
- 利用
Etag
能夠更加準確的控制快取,因為Etag
是伺服器自動生成或者由開發者生成的對應資源在伺服器端的唯一識別符號;Last-Modified
與ETag
是可以一起使用的,由於Etag
的優先度更高,所以伺服器會優先比較Etag
和If-None-Match
。一致的情況下,才會繼續比對Last-Modified
和If-Modified-Since
,最後才決定是否返回狀態碼304
。
3.快取策略
快取分類
強快取:
- 不會向伺服器傳送請求,直接從本地快取中獲取數;
- 請求資源的的狀態碼為:
200 ok(from memory cache)
;
協商快取:
- 向伺服器傳送請求,伺服器會根據請求頭的資源判斷是否命中協商快取;
- 如果命中,則返回狀態碼
304
通知瀏覽器從快取中讀取資源;
強快取與協商快取的區別
快取 | 獲取資源形式 | 狀態碼 | 傳送請求到伺服器 |
---|---|---|---|
強快取 | 從快取中獲取 | 200(from cache) | 否,直接從快取中獲取 |
協商快取 | 從快取中獲取 | 304(not modified) | 是,根據伺服器返回資訊判斷快取是否可用 |
分級快取策略
最下層的200
狀態
這一層由
Expires/Cache-Control
欄位控制:1.Expires
(HTTP1.0
版本有效)是絕對時間;2.Cache-Control
(HTTP1.1
版本有效)是相對時間;
當兩者都存在時,
Cache-Control
會覆蓋Expires
,只要這些欄位沒有失效,瀏覽器都會直接使用本地快取,屬於強快取;- 快取的來源大概有兩種
memory cache
和disk cache
:
可以看到,從memory cache
中讀取快取不需要時間,從disk cache
中讀取快取則需要一定時間。
相對時間與絕對時間與伺服器的設定有關,當伺服器設定Atime
(最後訪問時間)時,二者相等;當伺服器設定Mtime
(絕對修改時間)時,Expires
從資源的建立開始計算過期時間,Max-age
從請求發起的時間開始計算過期時間;
下圖便是淘寶中採用強快取的例子,狀態碼為200
,圖片資源都是從瀏覽器快取memory cache
中讀取,所以請求時間為0ms
:
中間的304
狀態
- 這一層由
last-modified/Etag
控制。當下一層失效時或使用者點選refresh/F5
時,瀏覽器就會向伺服器發起請求,如果伺服器上的相關資源沒有更新,則返回狀態碼304
,屬於協商快取;
下圖便為協商快取的情況,狀態碼為304
。也可以這樣理解:只要狀態碼是304
都屬於協商快取:
最上層的200
狀態
- 當瀏覽器本身沒有快取或者下一層失效時,或者使用者點選了
Ctrl + F5
強制重新整理時,瀏覽器會直接向伺服器請求最新的資源;
如下圖所示:
使用者行為對快取的影響
使用者操作 | Expires/Cache-Control | Last-Modified/Etag |
---|---|---|
位址列回車 | 有效 | 有效 |
頁面連結跳轉 | 有效 | 有效 |
新開視窗 | 有效 | 有效 |
前進後退 | 有效 | 有效 |
F5 重新整理 | 無效 | 有效 |
Ctrl + F5 強制重新整理 | 無效 | 無效 |
快取策略過程分析
如圖所示,該流程圖表示伺服器端在處理資源時採用快取策略的過程:
- 首先伺服器判斷資源是否可以複用,不可複用則在
Cache-Control
欄位中新增no-store
屬性; - 可以複用的情況下,判斷資源是否要求強一致?若是,則在
Cache-Control
欄位中新增no-cache
屬性,這樣不管快取資源是否過期,都要求客戶端或快取代理伺服器首先向伺服器確認資源的新鮮度,屬於協商快取; - 隨後伺服器指定是否允許
Web
代理快取資源(比如CDN
伺服器快取),如果允許則在Cache-Control
欄位中新增public
屬性,並指定代理快取伺服器上資源的有效期s-maxage
;不允許則新增private
屬性,表示只能由客戶端瀏覽器快取資源,並設定快取的有效期max-age
; - 隨後,根據情況在客戶端瀏覽器中,選擇強快取或者協商快取;
九、服務端效能最佳化
1.CDN
伺服器
定義
網站通常將其所有的伺服器都放在同一個地方,當使用者群增加時,公司就必須在多個地理位置不同的伺服器上部署內容。為了縮短http
請求的時間,我們應該把大量的靜態資源放置的離使用者近一點。
內容釋出網路CDN
(Content Delivery Networks
)就是其中一種方式。CDN
是一組分佈在多個不同地理位置或網段的web
伺服器,用於更加有效的向使用者釋出內容。
基本思路
- 儘可能避開網際網路上有可能影響資料傳輸速度和穩定性的瓶頸和環節,使內容傳輸的更快、更穩定;
- 透過在網路各處放置節點伺服器,在現有的網際網路基礎之上構成一層智慧虛擬網路;
CDN
系統能夠實時地根據網路流量和各節點的連線、負載狀況以及到使用者的距離和響應時間等綜合資訊 將使用者的請求重新導向離使用者最近的服務節點上。
基礎架構
最簡單的CDN
網路由一個DNS
伺服器和幾臺快取伺服器組成:
- 1、當使用者點選網站頁面上內容的
URL
時,經過本地DNS
系統解析,DNS
系統最終會將域名的解析權交給CNAME
指向的CDN
專用的DNS
伺服器;
關於DNS
解析,不一定由DNS
伺服器響應,一般從快取中讀取。比如電腦快取、瀏覽器快取、路由器快取、執行商快取等。如果快取中沒有找到,才一級一級地查詢:本地DNS
-> 許可權DNS
-> 頂級DNS
-> 根DNS
。全球只有13
臺根DNS
伺服器。
- 2、
CDN
的DNS
伺服器將CDN
的全域性負載均衡裝置的IP
地址返回給使用者; - 3、使用者向
CDN
的全域性負載均衡裝置發起內容URL
訪問請求; - 4、
CDN
全域性負載均衡裝置根據使用者的IP
地址,以及使用者請求的內容URL
,選擇一臺使用者所屬區域的區域負載均衡裝置,告訴使用者向這臺裝置發起請求; 5、區域負載均衡裝置會為使用者選擇一臺合適的快取伺服器提供服務,選擇的依據包括:
- 根據使用者
IP
地址,判斷哪一臺伺服器距使用者最近; - 根據使用者所請求的
URL
中攜帶的內容名稱,判斷哪一臺伺服器上有使用者所需內容; - 查詢各個伺服器當前的負載情況,判斷哪一臺伺服器尚有服務能力;
基於以上這些條件的綜合分析之後,
CDN
區域負載均衡裝置會向CDN
全域性負載均衡裝置返回一臺CDN
快取伺服器的IP
地址。- 根據使用者
- 6、
CDN
全域性負載均衡裝置把伺服器的IP
地址返回給使用者; - 7、使用者向
CDN
快取伺服器發起請求,快取伺服器響應使用者請求,將使用者所需內容傳送到使用者終端;如果這臺CDN
快取伺服器上並沒有使用者想要的內容,但是區域均衡裝置依然將它分配給了使用者,那麼這臺CDN
伺服器就要向它的上一級快取伺服器請求內容,直至追溯到網站的源伺服器將內容拉到本地;
應用場景
網站站點/應用加速:
站點或者應用中大量靜態資源的加速分發,建議將站點內容進行動靜分離,動態檔案可以結合雲伺服器
ECS
,靜態資源如各型別圖片、html
、css
、js
檔案等,使用CDN
伺服器儲存,可以有效加速內容載入速度,輕鬆搞定網站圖片、短影片等內容分發。移動應用加速:
移動
APP
更新檔案(apk
檔案)分發,移動APP
內圖片、頁面、短影片、UGC
等內容的最佳化加速分發。提供httpDNS
服務,避免DNS
劫持並獲得實時精確的DNS
解析結果,有效縮短使用者訪問時間,提升使用者體驗。- 視音訊點播/大檔案下載分發加速;
- 影片直播加速;
總結
簡單點說CDN
伺服器相當於順豐快遞分佈於全國各地的倉庫,主倉庫將快遞運送到這些分倉庫,使用者可以就近取貨,由此加快了速度。
除此之外CDN
伺服器還有許多高階功能,比如防止DDOS
攻擊等,這裡就不展開了;
2.SSR(Server Side Rendering)
依賴現代框架如Vue
和React
構建的網站,往往會存在一定的問題,比如Vue
框架。
Vue
渲染面臨的問題
首屏渲染時,要先下載和解析app.js
(打包過後的Vue.js
)之後,才能開始渲染頁面。
最佳化方案
- 構建層模板編譯:將模板編譯的任務放在了構建層中完成,而不是瀏覽器;
- 資料無關的
Prerender
的方式; - 服務端渲染:即將瀏覽器端進行的運算的一部分轉移到伺服器端上;
通常採用服務端渲染(SSR
)的方式進行最佳化。所謂SSR
就是利用伺服器端優秀的計算能力,將一部分的頁面渲染任務交由伺服器端進行處理。以下為服務端渲染SSR
的流程圖:
服務端渲染可以很好地最佳化首屏渲染的問題;可以根據業務需求,適當地分配客戶端和伺服器端的渲染部分,綜合利用客戶端和伺服器端的計算能力,從而達到效能最佳化的目的。