引言
前端是龐大的,包括
HTML
、CSS
、Javascript
、Image
、Flash
等等各種各樣的資源。前端優化是複雜的,針對方方面面的資源都有不同的方式。那麼,前端優化的目的是什麼 ?
- 從使用者角度而言,優化能夠讓頁面載入得更快、對使用者的操作響應得更及時,能夠給使用者提供更為友好的體驗。
- 從服務商角度而言,優化能夠減少頁面請求數、或者減小請求所佔頻寬,能夠節省可觀的資源。
資源的合併與壓縮
1. 資源的合併(減少http請求數量)
如上圖所示,檔案不合並存在的問題:
- 檔案與檔案之間有插入的上行請求,增加了N-1個網路延遲。
- 丟包問題影響更嚴重,每次網路請求都會存在丟包的情況。
keep-alive
經過代理伺服器時可能會被斷開 ,不一定能完成狀態的保持。
但是進行檔案合併也是存在的問題:
首屏渲染問題
: 在進行js檔案合併後,檔案明顯變大、請求時間變長。當html網頁進行渲染時,若該渲染需要依賴這個js的話,那頁面渲染會等到js檔案請求之後回來才會繼續進行,導致首屏渲染時間延遲。快取失效問題
:若現在有a、b、c三個檔案合併,只要任意改變其中一個檔案都會導致這個合併檔案快取失效,所以檔案合併容易導致大面積快取失效。
對於檔案合併有以下三個建議:
- 公共庫合併 ,業務程式碼單獨處理。
- 不同頁面的js檔案單獨打包。
- 在真實場景做相應處理,怎麼合適怎麼來 。
如何進行檔案合併:
- 使用線上網站進行檔案合併 。
- 在構建階段,使用nodejs實現檔案合併。(
webpack
、gulp
、fis3
等)
2. 資源的壓縮(減少請求資源的大小)
-
HTML壓縮 HTML程式碼壓縮就是壓縮一些在文字檔案中有意義,但是在HTML中不顯示的字元,包括
空格
,製表符
,換行符
等,還有一些其他意義的字元,如HTML註釋
也可以被壓縮。 -
CSS壓縮 CSS程式碼壓縮就是對一些
無效程式碼刪除
和CSS語義合併
。對於我們來說無效程式碼可能是註釋,也可能是無效字元。 -
JS壓縮與混亂 JS程式碼壓縮就是對一些
無效字元的刪除
、剔除註釋
、程式碼語義的縮減和優化
(如變數名縮短等)和程式碼保護
。(程式碼進行壓縮與混亂防止程式碼邏輯被輕易窺探) -
如何進行HTML、CSS、JS壓縮
-
使用線上網站進行壓縮。
圖片的優化
1. 各種型別的圖片
不同格式圖片常用的業務場景:
jpg
有失真壓縮,壓縮率高,不支援透明 —— 大部分不需要透明圖片的業務場景png
支援透明,瀏覽器相容好 (可降階壓縮:png32、png24、png8) —— 大部分需要透明圖片的業務場景webp
壓縮程度更好,在ios webview有相容性問題 ,在android中支援比較好 —— 安卓開發svg
向量圖,程式碼內嵌,相對較小 —— 圖片樣式相對簡單的業務場景(小的icon、logo等)gif
支援透明、體積小、成像相對清晰(靜態GIF和動畫GIF) —— 與其他4種圖片的使用場景基本不衝突
壓縮後的大小:png > jpg > webp
一個好用的圖片優化平臺
2. 進行圖片壓縮
- Css雪碧圖:把一些圖片整合到一張單獨的圖片中
- 優點:減少你的網站的HTTP請求數量。
- 缺點:整合圖片比較大時,一次載入比較慢,替換圖片也比較麻煩。
- Image inline:將圖片的內容內嵌到html當中
- 優點:減少你的網站的HTTP請求數量。
- 缺點:適合比較小的圖。
- 使用SVG向量圖
CSS和JS的裝載與執行
1. HTML渲染過程的特點
- 順序執行
- 進行詞法分析,從上到下。
- 併發載入
- 外部資源併發請求,併發度受瀏覽器域名限制,單個域名併發度是有限制的。
- 是否阻塞
- css在head中通過link方式引入的話會阻塞頁面的渲染。
- css不阻塞外部資源的載入 ,會阻塞js的執行。
- js的引入會阻塞html文件的分析、頁面的渲染。
- js不阻塞外部資源的載入。
- js順序執行,阻塞後續js邏輯的執行。
- 依賴關係
- 頁面渲染依賴於css的載入。
- js的執行順序是有依賴關係。
- js邏輯對於dom節點的依賴關係。
- 引入方式(js)
- 指令碼直接引入:瀏覽器會立即載入並執行指令碼,js的載入和執行會阻塞頁面的渲染。(同步)
- defer方式引入:不會阻塞頁面的渲染,執行時間延遲到dom樹構建完成後(
DOMContentLoaded
事件觸發之前完成,保證能拿到dom元素),順序執行。(非同步) - async方式引入:不會阻塞頁面的渲染,
onload
事件觸發前執行,不保證執行順序,指令碼一旦載入完畢就會立刻執行。(非同步) - 動態建立script方式:程式碼動態建立script方式引入,將script標籤插入到DOM中。(非同步)
DOMContentloaded與onload的區別
- 當onload事件觸發時,頁面上所有的DOM、樣式表、指令碼、圖片、flash都已經載入完成。
- 當DOMContentloaded事件觸發時,僅當DOM載入完成,不包括樣式表、指令碼、圖片、flash。
2. 載入和執行的一些優化點
- css 樣式表置頂
- 會阻塞頁面的渲染,防止頁面在沒有css樣式的情況下渲染出來。
- 用link代替import引入css
- import不會觸發瀏覽器併發機制,頁面載入渲染完成之後才會進行import的工作(目前有的高版本的瀏覽器,link和import的作用沒有區別),但是引用層數還是有影響併發。
- js 指令碼置底
- 不阻塞頁面的渲染和css的載入,使css資源儘量在第一批併發下載中,讓頁面更快的呈現給使用者。
- 合理使用js的非同步載入
懶載入和預載入
1. 懶載入
- 原理
- 當圖片進入可視區域後請求圖片資源,需要去監聽
scroll
事件的回撥,去判斷我們的懶載入圖片是否進入可視區域。 - 注意:
getBoundingClientRect
用於獲取某個元素相對於視窗的位置集合,若圖片top
小於視窗的高度,說明進入可視區域。
- 優點
- 減少無效資源的載入。
- 併發載入資源過多會阻塞js的載入,影響網站的正常使用。
- 實現方式
- 自己編寫懶載入程式碼
lazyload.js
。
<!doctype html>
<html>
<head>
<title>懶載入</title>
</head>
<body>
<div class="image-list">
<img src="" class="image-item" lazyload="true" data-original="http://xxx.xxx.1.jpg" />
<img src="" class="image-item" lazyload="true" data-original="http://xxx.xxx.2.jpg" />
<img src="" class="image-item" lazyload="true" data-original="http://xxx.xxx.3.jpg" />
<img src="" class="image-item" lazyload="true" data-original="http://xxx.xxx.4.jpg" />
<img src="" class="image-item" lazyload="true" data-original="http://xxx.xxx.5.jpg" />
<img src="" class="image-item" lazyload="true" data-original="http://xxx.xxx.6.jpg" />
<img src="" class="image-item" lazyload="true" data-original="http://xxx.xxx.7.jpg" />
<img src="" class="image-item" lazyload="true" data-original="http://xxx.xxx.8.jpg" />
<img src="" class="image-item" lazyload="true" data-original="http://xxx.xxx.9.jpg" />
<img src="" class="image-item" lazyload="true" data-original="http://xxx.xxx.10.jpg" />
</div>
<script>
var viewHeight = document.documentElement.clientHeight; //獲取可視區域的高度
function lazyload () {
var eles = document.querySelectorAll('img[data-original][lazyload]'); //獲取需要懶載入的元素
Array.prototype.forEach.call(eles, function (item, index) {
var rect;
if (item.dataset.original === '') return;
rect = item.getBoundingClientRect(); //獲取元素的大小及其相對於視口的位置集合,集合中有top, right, bottom, left等屬性。
if (rect.bottom >= 0 && rect.top < viewHeight) { //判斷元素是否進入可視區域
!function () { //立即執行匿名函式,載入圖片
var img = new Image();
img.src = item.dataset.original;
img.onload = function () {
item.src = img.src;
}
item.removeAttribute('data-original');
item.removeAttribute('lazyload');
}()
}
})
}
lazyload(); //初始化懶載入方法
document.addEventListener('scroll', lazyload); //新增頁面滾動監聽器
</script>
</body>
</html>
複製程式碼
- 使用網上分享的
lazyload
庫。
2. 預載入
- 原理
- 圖片等靜態資源在使用前提前請求。
- 優點
- 資源使用時能從快取中載入,提升使用者體驗。
- 實現方式
- <img src="http://..." style="display: none" />
- 使用
Image物件
,var image = new Image();image.src = "http://..."; - 使用
XMLHttpRequest
物件,更好去控制預載入的過程,存在跨域問題。 - 使用PreloadJS,提供了一個一致的方式預先載入在HTML應用的內容,以及預載入可以使用HTML標籤作為XHR完成。
迴流與重繪
1. css效能讓javascript變慢
- 頻繁觸發重繪與迴流,會導致UI頻繁渲染,最終導致js變慢。
2. 迴流
- 當render tree中的一部分(或全部)因為元素的規模尺寸,佈局,隱藏等改變而需要重新構建,這就稱為迴流(
reflow
)。當頁面佈局和幾何屬性改變時就需要回流。
3. 重繪
- 當render tree中的一些元素需要更新屬性,而這些屬性只是影響元素的外觀,風格,而不會影響佈局的,比如background-color。則就稱為重繪(
repaint
)。
4. 迴流與重繪關係
迴流必將引起重繪,而重繪不一定會引起迴流。
5. 觸發頁面重佈局(迴流)的屬性
6. 只觸發重繪的屬性
7. 新建DOM的過程
1、獲取DOM後分隔為多個圖層
2、對每個圖層的節點計算樣式結果(recalculate style)
3、為每個節點生成圖形和位置(layout、reflow和重佈局)
4、將每個節點繪製填充到圖層點陣圖彙總(paint,repaint)
5、圖層作為紋理載入到GPU
6、合併多個圖層到頁面上,生成最終影象(composite layers)
複製程式碼
8. Chrome建立圖層的條件
1、3D或透視變換(perspective、transform)CSS屬性
2、使用加速視訊解碼的<video>節點
3、擁有3D(WebGL)上下文或加速的2D上下文的<canvas>節點
4、混合外掛(如Flash)
5、對自己的opacity做CSS動畫或使用一個動畫webkit變換的元素
6、擁有加速CSS過濾器的元素
7、元素有一個包含複合層的後代節點(一個元素擁有一個子元素,該子元素在自己的層裡)
8、元素有一個z-index較低且包含一個複合層的兄弟元素(換句話說就是該元素在複合層上面渲染)
複製程式碼
9. 實戰優化點
1、用translate(重繪)替代top(迴流)改變
2、用opacity替代visibility(重繪)
3、不要一條一條地修改 DOM 的樣式,預先定義好 class,然後修改 DOM 的className
4、把 DOM 離線後修改,比如:先把 DOM 給 display:none (有一次迴流),然後你修改100次,然後再把它顯示出來
5、不要把獲取DOM元素的真實位置程式碼放在一個迴圈裡使用,否則會對相關緩衝區進行重新整理,最好存到迴圈外的變數中再去使用
6、不要使用table佈局,可能很小的一個小改動會造成整個 table 的重新佈局(迴流)
7、動畫實現的速度的選擇,動畫速度快(重繪和迴流)的話可能導致頁面效能下降
8、對於動畫新建圖層(例:新增transform CSS屬性)
9、新增 CSS3 樣式啟用 GPU 硬體加速(例:transform: translateZ(0)或transform: translate3d(0, 0, 0))
10、減少對DOM的操作,對DOM操作的代價是高昂的
11、避免使用出發重繪、迴流的CSS屬性
12、將重繪、迴流的影響範圍限制在單獨的圖層之內,但是圖層的合成過程比較消耗運算量,圖層不能過多
複製程式碼
瀏覽器儲存
1. cookie
- 因為HTTP請求無狀態,所以需要
cookie
去維持客戶端狀態。 - 可以設定過期時間
expire
。 - cookie的兩種生成方式及作用
http response header
中的set-cookie(服務端生成),用於瀏覽器端和伺服器端的互動。- js中可以通過
document.cookie
可以讀寫cookie(客戶端生成),客戶端自身資料的儲存。
- 僅僅作為瀏覽器儲存。(大小4KB左右,能力被
localstorage
替代) - 所有相關域名請求都會帶上cookie,有的請求不需要cookie,造成
cdn
上靜態檔案的流量損耗(將cdn域名和主域名獨立開)。 httponly
,不允許js進行讀寫,防止攻擊。
2. LocalStorage和SessionStorage
- LocalStorage
- HTML5設計出來專門用於瀏覽器儲存的。(沒有時間限制)
- 大小為5M左右。
- 僅在客戶端使用,不和服務端進行通訊。
- 介面封裝較好,讀寫、刪除資料方便。
- 瀏覽器本地快取方案。
- SessionStorage
- 會話級別的瀏覽器儲存 (瀏覽器一個標籤頁就是一個會話,當籤頁關閉後資料清空)。
- 大小為5M左右。
- 僅在客戶端使用,不和服務端進行通訊。
- 介面封裝較好,讀寫、刪除資料方便。
- 適合用於對錶單資訊的維護。
3. IndexedDB
- IndexedDB 是一種低階API,用於客戶端儲存大量結構化資料。該API使用索引來實現對該資料的高效能搜尋。雖然 Web Storage 對於儲存較少量的資料很有用,但對於儲存更大量的結構化資料來說,這種方法不太有用。IndexedDB提供了一個解決方案。
- 為應用建立離線版本。
4. PWA (Progressive Web Apps)
- 簡介
- PWA (Progressive Web Apps) 是一種 Web App 新模型,並不是具體指某一種前沿的技術或者某一個單一的知識點,我們從英文縮寫來看就能看出來,這是一個漸進式的 Web App,是通過一系列新的 Web 特性,配合優秀的 UI 互動設計,逐步的增強 Web App 的使用者體驗。
- 特點
可靠
:在沒有網路的環境中也能提供基本的頁面訪問,而不會出現“未連線到網際網路”的頁面。快速
:針對網頁渲染及網路資料訪問有較好優化。融入
:應用可以被增加到手機桌面,並且和普通應用一樣有全屏、推送等特性。
- 缺點
- 門檻不低(要求
HTTPS
,Service Worker 的 API 比較 low-level) - 瀏覽器支援不夠完美(Safari 短期內不會支援,在 5 年計劃裡提了一嘴)
- 使用者習慣 (讓使用者習慣於網頁可以離線工作並不是短期可以達到的)
- 效能檢測工具
Lighthouse
,可以檢測網站是否符合PWA、網站的可靠性、速度等效能優化指標[下載地址]。(https://lavas.baidu.com/doc-lavas/vue/more/downloads/lighthouse_2.1.0_0.zip)
5. Service Worker
- 簡介
- Service Worker 是一個指令碼,瀏覽器獨立於當前網頁,將其在後臺執行,為實現一些不依賴頁面或者使用者互動的特性開啟了一扇大門。在未來這些特性將包括推送訊息,背景後臺同步,
geofencing
(地理圍欄定位),但它將推出的第一個首要特性,就是攔截和處理網路請求的能力,包括以程式設計方式來管理被快取的響應。Service Worker只能用於https站點中,非https站點不具備Service Worker能力。
-
生命週期
-
運用
- 使用攔截和處理網路請求的能力,去實現一個離線應用。
- 使用Service Worker在後臺執行同時能和頁面通訊的能力,去實現大規模後臺資料的處理。
- 檢測
- 檢視當前瀏覽器上執行的Service Worker (
chrome://inspect/#service-workers
)。 - 檢視已註冊的Service Worker (
chrome://serviceworker-internals
)。
快取
1. 原理
瀏覽器快取就是把一個已經請求過的Web資源(如html頁面
,圖片
,js
,資料
等)拷貝一份副本儲存在瀏覽器中。快取會根據進來的請求儲存輸出內容的副本。當下一個請求來到的時候,如果是相同的URL,快取會根據快取機制決定是直接使用副本響應訪問請求,還是向源伺服器再次傳送請求。比較常見的就是瀏覽器會快取訪問過網站的網頁,當再次訪問這個URL地址的時候,如果網頁沒有更新,就不會再次下載網頁,而是直接使用本地快取的網頁。只有當網站明確標識資源已經更新,瀏覽器才會再次下載網頁。
2. 快取的好處
- 減少網路頻寬消耗
- 無論對於網站運營者或者使用者,頻寬都代表著金錢,過多的頻寬消耗,只會便宜了網路運營商。當Web快取副本被使用時,只會產生極小的網路流量,可以有效的降低運營成本。
- 降低伺服器壓力
- 給網路資源設定有效期之後,使用者可以重複使用本地的快取,減少對源伺服器的請求,間接降低伺服器的壓力。同時,搜尋引擎的爬蟲機器人也能根據過期機制降低爬取的頻率,也能有效降低伺服器的壓力。
- 減少網路延遲,加快頁面開啟速度
- 頻寬對於個人網站運營者來說是十分重要,而對於大型的網際網路公司來說,可能有時因為錢多而真的不在乎。那Web快取還有作用嗎?答案是肯定的,對於終端使用者,快取的使用能夠明顯加快頁面開啟速度,達到更好的體驗。
3. 瀏覽器請求流程
4. 快取策略
- Expires策略
- Expires是Web伺服器響應訊息頭欄位,在響應http請求時告訴瀏覽器在過期時間前瀏覽器可以直接從瀏覽器快取取資料,而無需再次請求。
- Expires是
HTTP1.0
的東西,現在預設瀏覽器均預設使用HTTP1.1
,所以它的作用基本忽略。 - Expires的一個缺點就是返回的到期時間是伺服器端的時間,這樣存在一個問題,如果客戶端的時間與伺服器的時間相差很大(比如時鐘不同步,或者跨時區),那麼誤差就很大,所以在HTTP1.1版開始,使用
Cache-Control: max-age=秒
替代。
- Cache-control策略:Cache-control對應值可以是
public
、private
、no-cache
、no-store
、no-transform
、must-revalidate
、proxy-revalidate
、max-age
、s-maxage
- 請求Request:
1、no-cache:不要讀取快取中的檔案,要求向WEB伺服器重新請求
2、no-store:請求和響應都禁止被快取
3、max-age:表示當訪問此網頁後的max-age秒內再次訪問不會去伺服器請求,其功能與Expires類似,只是 Expires是根據某個特定日期值做比較。一但快取者自身的時間不準確.則結果可能就是錯誤的,而max-age, 顯然無此問題.。Max-age的優先順序也是高於Expires的
4、max-stale:允許讀取過期時間必須小於max-stale 值的快取物件
5、min-fresh:接受其max-age生命期大於其當前時間 跟 min-fresh 值之和的快取物件
6、only-if-cached:告知快取者,我希望內容來自快取,我並不關心被快取響應,是否是新鮮的
7、no-transform:告知代理,不要更改媒體型別,比如jpg,被你改成png
- 響應Response:
1、public: 資料內容皆被儲存起來,就連有密碼保護的網頁也儲存,安全性很低
2、private:資料內容只能被儲存到私有的cache,僅對某個使用者有效,不能共享
3、no-cache:可以快取,但是隻有在跟WEB伺服器驗證了其有效後,才能返回給客戶端
4、no-store:請求和響應都禁止被快取
5、max-age:本響應包含的物件的過期時間
6、must-revalidate:如果快取過期了,會再次和原來的伺服器確定是否為最新資料,而不是和中間的proxy
7、s-maxage:與max-age的唯一區別是,s-maxage僅僅應用於共享快取.而不應用於使用者代理的本地快取等針對單使用者的快取。另外,s-maxage的優先順序要高於max-age
8、max-stale:允許讀取過期時間必須小於max-stale 值的快取物件
9、proxy-revalidate:與must-revalidate類似,區別在於proxy-revalidate要排除掉使用者代理的快取的。即其規則並不應用於使用者代理的本地快取上
10、no-transform:告知代理,不要更改媒體型別,比如jpg,被你改成png
- Last-Modified/If-Modified-Since
- Last-Modified:標示這個響應資源的最後修改時間。web伺服器在響應請求時,告訴瀏覽器資源的最後修改時間。
- If-Modified-Since:當資源過期時(使用Cache-Control標識的max-age),發現資源具有Last-Modified宣告,則再次向web伺服器請求時帶上頭If-Modified-Since表示請求時間。web伺服器收到請求後發現有頭If-Modified-Since則與被請求資源的最後修改時間進行比對。若最後修改時間較新,說明資源又被改動過,則響應整片資源內容(寫在響應訊息包體內),HTTP 200;若最後修改時間較舊,說明資源無新修改,則響應HTTP 304(無需包體,節省瀏覽),告知瀏覽器繼續使用所儲存的cache。
- 注意:Last-Modified標註的最後修改只能精確到秒級,如果某些檔案在1秒鐘以內,被修改多次的話,它將不能準確標註檔案的修改時間。如果某些檔案會被定期生成,當有時內容並沒有任何變化,但Last-Modified卻改變了,導致檔案沒法使用快取。有可能存在伺服器沒有準確獲取檔案修改時間,或者與代理伺服器時間不一致等情形。Etag是伺服器自動生成或者由開發者生成的對應資源在伺服器端的唯一識別符號,能夠更加準確的控制快取。Last-Modified與ETag一起使用時,伺服器會優先驗證ETag。
- Etag/If-None-Match
- Etag:web伺服器響應請求時,告訴瀏覽器當前資源在伺服器的唯一標識(生成規則由伺服器決定)。Apache中,ETag的值,預設是對檔案的索引節(INode),大小(Size)和最後修改時間(MTime)進行Hash後得到的。
- If-None-Match:當資源過期時(使用Cache-Control標識的max-age),發現資源具有Etage宣告,則再次向web伺服器請求時帶上頭If-None-Match (Etag的值)。web伺服器收到請求後發現有頭If-None-Match 則與被請求資源的相應校驗串進行比對,決定返回200或304。
- 快取策略使用者行為與快取