首頁>技術>

當我們去面試的時候,很大機率會被面試官問這麼一個問題:你有嘗試過對專案做效能最佳化嗎?或者你瞭解哪些效能最佳化的方法?聽到這個問題的你可能是這樣的:

似曾相識但又說不清楚,往往只能零散地說出那麼幾點,難以做到有條理的回答。那麼,本文就帶你簡單瞭解前端效能最佳化的幾個主要方面,旨在拋磚引玉。

一、資源的合併和壓縮

web前端應用的開發與部署過程:

輸入url到頁面顯示出來的過程:

請求過程中一些潛在的效能最佳化點:

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壓縮與混亂(醜化)

包括:

使用線上網站進行壓縮:https://tool.oschina.net/jscompress/使用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.圖片壓縮

針對真實圖片情況,捨棄一些相對無關緊要的色彩資訊,對圖片進行壓縮;比如線上壓縮網站:https://tinypng.com/

2.css雪碧圖

將網站上用到的一些圖片整合到一張單獨的圖片中,從而減少網站HTTP請求數量。原理為:設定整張雪碧圖可示區域,將想要顯示的圖示定位到該處(左上角);缺點:整合圖片比較大時,一次載入比較慢。

如天貓的雪碧圖:

很多情況下,並不是所有的小圖示都放在一張雪碧圖中,而是會適當進行拆分。現在使用雪碧圖的場景比較少了。

自動生成雪碧圖樣式的網站:http://www.spritecow.com/

選中雪碧圖中對應的圖示,就會生成對應的樣式。

3.網頁內聯圖片(Image inline)

將圖片的內容內嵌到html當中,減少網站的HTTP請求數量,常用於處理小圖示和背景圖片。網頁內聯圖片寫法為:

 <imgsrc="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA..."alt="">

瀏覽器上的表現形式為:

這裡提供一個將:image 轉 DataUrI的網址:http://tool.c7sky.com/datauri/

缺點:

瀏覽器不會快取內聯圖片資源;相容性較差,只支援ie8以上瀏覽器;超過1000kb的圖片,base64編碼會使圖片大小增大,導致網頁整體下載速度減慢;

所以要根據場景使用,不過內聯圖片減少HTTP請求的優點還是很顯著的。比如,在開發中小於4KB或8KB的圖片都會透過構建工具自動inline到HTML中,這種情況下Image inline帶來的圖片大小增長其實是比增加HTTP請求次數更優的。

4.向量圖SVG與iconfont

使用iconfont解決icon問題

應儘量使用該方式,比如可以採用阿里巴巴向量相簿:

可以選擇格式進行下載:

可以看到它們的大小有著明顯的差異:

使用SVG進行向量圖的控制

SVG 意為可縮放向量圖形(Scalable Vector Graphics)。SVG 使用 XML 格式定義影象。下為示例:

線上轉換網站:http://www.bejson.com/convert/image_to_svg/

5.webp

webp的優勢體現在它具有更優的影象壓縮演算法,能帶來更小的圖片體積,而且擁有肉眼識別無差異的影象質量;同時具備了無損和有損的壓縮模式、Alpha透明以及動畫的特性。在JPEG和PNG上的轉化效果都非常優秀、穩定和統一。安卓上不存在相容性問題,推薦安卓下使用。

以下為淘寶網首頁請求的圖片:

可以看到,圖片中大量地添加了webp格式的選擇。.jpg_.webp表示當瀏覽器支援webp時採用webp格式,否則採用jpg格式。

下面為B站首頁的圖片,可以看到基本都採用了webp格式:

同一張圖片jpg格式和webp格式壓縮率有著明顯的差異:

可以透過線上網站將圖片轉換為webp:https://zhitu.isux.us/

像圖片這樣的靜態檔案可以存放在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來更新網頁。

補充:

1. HTML中可能會引入很多的css、js這樣的外部資源,這些外部資源在瀏覽器端是併發載入的。但是瀏覽器會對同一域名進行併發數量(度)的限制,即單個域名的併發度是有限的;

2. 所以,經常將大部分的資源託管到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)opacityfilterwill-change:哪一個屬性即將發生變化,進而進行最佳化。3.重繪(Repaint)

重繪是一個元素外觀的改變所觸發的瀏覽器行為,比如background-color、outline等屬性。這些屬性不影響佈局,隻影響元素的外觀風格,會造成DOM元素的重新渲染,這個過程稱為重繪。

需要注意的是:重繪是以圖層為單位,如果圖層中某個元素需要重繪,那麼整個圖層都需要重繪。比如一個圖層包含很多節點,其中有個gif圖,gif圖的每一幀,都會重回整個圖層的其他節點,然後生成最終的圖層點陣圖。

因此,可以透過特殊的方式來強制gif圖單獨為一個圖層(translateZ(0)或者translate3d(0,0,0);CSS3的動畫也是一樣(好在絕大部分情況瀏覽器自己會為CSS3動畫的節點建立圖層);

所以:頻繁重繪迴流的DOM元素作為一個獨立圖層,那麼這個DOM元素的重繪和迴流只會該圖層;原則上是要儘量避免新建圖層的,因為這會導致圖層重組(Composive Layers)時候的計算量增大。所以,只有當某些DOM元素頻繁重繪迴流時,才新建一個獨立圖層放置它們;

只會觸發重繪的屬性

//部分屬性colorborder-styleborder-radiusvisibilitytext-decorationbackgroundbackground-imagebackground-positionbackground-repeatbackground-sizeoutline-coloroutlineoutline-styleoutline-widthbox-shadow
4.迴流(Reflow)

當render tree中的一部分(或全部)因為元素的規模尺寸佈局隱藏等改變而需要重新構建。這就稱為迴流(reflow);

當頁面佈局和幾何屬性改變時就需要回流;迴流必將引起重繪,而重繪不一定會引起迴流;

觸發頁面重佈局(迴流)的屬性

頻繁觸發重繪迴流,會導致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屬性新建圖層;

再次檢視此時的圖層情況,可以看到此時首頁的圖層非常之多,十分地卡:

17
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • Zabbix監控Oracle帳號的狀態_自動發現