前端性能優(yōu)化

原文鏈接:https://blog.csdn.net/weixin_44730897/article/details/111247844

一、優(yōu)化思路

前端性能優(yōu)化手段從以下幾個方面入手:
加載優(yōu)化,執(zhí)行優(yōu)化,渲染優(yōu)化,腳本優(yōu)化、代碼優(yōu)化
1、加載優(yōu)化:減少HTTP請求、緩存資源、壓縮代碼、無阻塞、首屏加載、按需加載、預(yù)加載、壓縮圖像、減少Cookie、避免重定向、異步加載第三方資源。
2、執(zhí)行加載:
CSS寫在頭部,JS寫在尾部并異步、避免img、iframe等的src為空、盡量避免重置圖像大小、圖像盡量避免使用DataURL
3、渲染加載:
設(shè)置viewport、減少DOM節(jié)點(diǎn)、優(yōu)化動畫、優(yōu)化高頻事件、GPU加速
4、樣式優(yōu)化:
避免在HTML中書寫style、避免CSS表達(dá)式、移除CSS空規(guī)則、正確使用display:display、不濫用float等
5、腳本優(yōu)化:
減少重繪和回流、緩存DOM選擇與計(jì)算、緩存.length的值、盡量使用事件代理、盡量使用id選擇器、touch事件優(yōu)化.

二、分塊詳解

2.1、加載優(yōu)化

1、減少http請求,盡量減少頁面的請求數(shù)。(首次加載同時請求數(shù)不能超過4個)
(1):合并CSS和JS
(2):使用CSS精靈圖
2、緩存資源:使用緩存可減少向服務(wù)器的請求數(shù),節(jié)省加載時間,所有靜態(tài)資源都要在服務(wù)器端設(shè)置緩存,并且盡量使用長緩存(使用時間戳更新緩存)。
(1):緩存一切可緩存的資源
(2):使用長緩存
(3):使用外聯(lián)的樣式和腳本
3、壓縮代碼:減少資源大小可加快網(wǎng)頁顯示速度,對代碼進(jìn)行壓縮,并在服務(wù)器端設(shè)置GZip
(1):壓縮代碼(多余的縮進(jìn)、空格和換行符)
(2):啟用Gzip
4、無阻塞:頭部內(nèi)聯(lián)的樣式和腳本會阻塞頁面的渲染,樣式放在頭部并使用link方式引入,腳本放在尾部并使用異步方式加載。
5、首屏加載:首屏快速顯示可大大提升用戶對頁面速度的感知,應(yīng)盡量針對首屏的快速顯示做優(yōu)化
6、按需加載:將不影響首屏的資源和當(dāng)前屏幕不用的資源放到用戶需要時才加載,可大大提升顯示速度和降低總體流量(按需加載會導(dǎo)致大量重繪,影響渲染性能)
(1):按需加載
(2):滾動加載
(3):Media Query加載
7、預(yù)加載:大型資源頁面可使用Loading,資源加載完成后再顯示頁面,但加載時間過長,會造成用戶流失
(1):可感知Loading:進(jìn)入頁面時Loading
(2):不可感知Loading:提前加載下一頁
8、壓縮圖像:使用圖像時選擇最合適的格式和大小,然后使用工具壓縮,同時在代碼中用srcset來按需顯示過度壓縮圖像大小影響圖像顯示效果。
(1):使用TinyJpg和TinyPng壓縮圖像
(2):使用CSS3、SVG、IconFont代替圖像
(3):使用img的srcset按需加載圖像
(4):選擇合適的圖像:webp優(yōu)于jpg,png8優(yōu)于gif
(5):選擇合適的大?。菏状渭虞d不大于1014kb、不寬于640pxPS切圖時D端圖像保存質(zhì)量為80,M端圖像保存質(zhì)量為60
9、減少cookie:cookie影響加載速度,靜態(tài)資源域名不使用cookie.
10、避免重定向:重定向會影響加載速度,在服務(wù)器正確設(shè)置避免重定向
11、異步加載第三方資源:第三方資源不可控會影響頁面的加載和顯示,要異步加載第三方資源。

2.2、執(zhí)行優(yōu)化:

1、css寫在頭部,js寫在尾部并異步。
2、避免img、iframe等的src為空:空src會重新加載當(dāng)前頁面,影響速度和效率。
3、盡量避免重置圖像大?。憾啻沃刂脠D像大小會引發(fā)圖像的多次重繪,影響性能
4、圖像盡量避免使用DataURL:DataURL圖像沒有使用圖像的壓縮算法,文件會變大,并且要解碼后再渲染,加載慢耗時長。

2.3、渲染優(yōu)化:

1、設(shè)置viewport:HTML的viewport可加速頁面的渲染
2、減少DOM節(jié)點(diǎn):DOM節(jié)點(diǎn)太多影響頁面的渲染,盡量減少DOM節(jié)點(diǎn)。

3、優(yōu)化動畫
(1):盡量使用css3動畫
(2):合理使用requestAnimationFrame動畫代替setTimeout.
(3):適當(dāng)使用canvas動畫:5個元素以內(nèi)使用css動畫,5個元素使用canvas動畫,iOS8+可使用WebGL動畫。

4、優(yōu)化高頻事件:scroll、touchmove等事件可導(dǎo)致多次渲染。
(1):函數(shù)節(jié)流
(2):函數(shù)防抖
(3):使用requestAnimationFrame監(jiān)聽幀變化:使得在正確的時間進(jìn)行渲染
(4):增加響應(yīng)變化的時間間隔:減少重繪次數(shù)

5、GPU加速:使用某些html5標(biāo)簽和css3屬性觸發(fā)GPU渲染,請合理使用(過渡使用會引發(fā)手機(jī)耗電量增加)。
(1):html標(biāo)簽:video,canvas,webgl
(2):css屬性,opacity,transform,transition

2.4、樣式優(yōu)化:

1、避免在HTML中書寫style
2、避免CSS表達(dá)式:CSS表達(dá)式的執(zhí)行需跳出CSS樹的渲染
3、移除CSS空規(guī)則:CSS空規(guī)則增加了css文件的大小,影響CSS樹的執(zhí)行
4、正確使用display:display會影響頁面的渲染
display:inline后不應(yīng)該再使用float、margin、
padding、width和height
display:inline-block后不應(yīng)該再使用float
display:block后不應(yīng)該再使用vertical-align
display:table-*后不應(yīng)該再使用float和margin
5、不濫用float:float在渲染時計(jì)算量比較大,盡量減少使用
6、不濫用Web字體:Web字體需要下載、解析、重繪當(dāng)前頁面,盡量減少使用
7、不聲明過多的font-size:過多的font-size影響CSS樹的效率
8、值為0時不需要任何單位:為了瀏覽器的兼容性和性能,值為0時不要帶單位
9、標(biāo)準(zhǔn)化各種瀏覽器前綴
無前綴屬性應(yīng)放在最后
CSS動畫屬性只用-webkit-、無前綴兩種
其它前綴為-webkit-、-moz-、-ms-、無前綴四種:
Opera改用blink內(nèi)核,-o-已淘汰
避免讓選擇符看起來像正則表達(dá)式:高級選擇符執(zhí)行耗時長且不易讀懂,避免使用

2.5、腳本優(yōu)化:

1、減少重繪和回流
避免不必要的DOM操作
避免使用document.write
減少drawImage
盡量改變class而不是style,使用classList代替
className
2、緩存DOM選擇與計(jì)算:每次DOM選擇都要計(jì)算和緩存
3、緩存.length的值:每次.length計(jì)算用一個變量保存值
4、盡量使用事件代理:避免批量綁定事件
5、盡量使用id選擇器:id選擇器選擇元素是最快的
6、touch事件優(yōu)化:使用tap(touchstart和touchend)代替click(注意touch響應(yīng)過快,易引發(fā)誤操作)。

2.6、代碼優(yōu)化

2.6.1、lighthouse主線程優(yōu)化

lighthouse上顯示主線程耗時最多的是樣式和布局,所以對這部分進(jìn)行優(yōu)化。主要有一下幾點(diǎn):
1、去掉頁面上用于布局的table,table本身性能較低,且維護(hù)性差,是一種過時的布局方案。
2、在去掉table布局的同時減少一些無意義的DOM元素,減少DOM元素的數(shù)量和嵌套。
3、減少css選擇器的嵌套。用sass,less這種css預(yù)處理器很容易造成多層嵌套。優(yōu)化前代碼里最多的有七八層嵌套,對性能有一定影響。重構(gòu)后不超過三層。

2.6.2、js代碼的優(yōu)化和重構(gòu)

移除Vue框架以及用服務(wù)端端直出,現(xiàn)在js代碼已經(jīng)減少了大部分。主要有以下幾部分:
1、拆分函數(shù),將功能復(fù)雜的函數(shù)拆分成小函數(shù),讓每個函數(shù)只做一件事。
2、優(yōu)化分支結(jié)構(gòu),用對象Object,代替if…else和switch…case

2.6.3、優(yōu)化DOM操作

DOM操作如改變樣式,改變內(nèi)容可能會引起頁面的重繪重排,是比較消耗性能的。網(wǎng)上也有很多優(yōu)化jq操作的方法。
如將查詢到的DOM使用變量存起來,避免重復(fù)查詢。以及將多次DOM操作變成一次等。
這里重點(diǎn)講一下第二種。常見的需求是渲染一個列表,如果直接在for循環(huán)里面append到父元素中,性能是非常差的。原來的操作是將所有DOM用字符串拼接起來,再用html()方法一次性添加到頁面中。
還有另一種方法是使用文檔碎片(fragment)。通過document.createDocumentFragment()可以新建一個虛擬的節(jié)點(diǎn)對象。
它有一個很實(shí)用的特點(diǎn),當(dāng)請求把一個DocumentFragment節(jié)點(diǎn)插入文檔樹時,插入的不是DocumentFragment自身,而是它的所有子孫節(jié)點(diǎn),即插入的是括號里的節(jié)點(diǎn)。這個特性使得DocumentFragment成了占位符,暫時存放那些一次插入文檔的節(jié)點(diǎn)。它還有利于實(shí)現(xiàn)文檔的剪切、復(fù)制和粘貼操作。
當(dāng)需要添加多個dom元素時,如果先將這些元素添加到DocumentFragment中,再統(tǒng)一將DocumentFragment添加到頁面,會減少頁面渲染dom的次數(shù),效率會明顯提升。因?yàn)槲臋n片段存在于內(nèi)存中,并不在DOM中,所以將子元素插入到文檔片段中時不會引起頁面回流(對元素位置和幾何上的計(jì)算),因此使用DocumentFragment可以起到性能優(yōu)化的作用。
經(jīng)過測試,在當(dāng)前的場景下,使用fragment的速度和html()是差不多的,都是10ms左右。區(qū)別在于最后將fragment添加到頁面上$(‘.container’).append(fragment)這行代碼僅僅花費(fèi)1ms。也就是說,將fragment插入頁面時不會引起頁面重繪重排,不會引起阻塞。

var ul = document.getElementById("ul");
var fragment = document.createDocumentFragment();
for (var i = 0; i < 20; i++) {
    var li = document.createElement("li");
    li.innerHTML = "index: " + i;
    fragment.appendChild(li);
}
ul.appendChild(fragment);

2.6.4、尾調(diào)用

尾調(diào)用是指某個函數(shù)的最后一步是調(diào)用另一個函數(shù)。
函數(shù)調(diào)用會在內(nèi)存形成一個“調(diào)用記錄”,又稱“調(diào)用幀”,保存調(diào)用位置和內(nèi)存變量等信息。如果在函數(shù)A的內(nèi)部調(diào)用函數(shù)B,那么在A的調(diào)用幀上方,還會形成一個B的調(diào)用幀。等到B運(yùn)行結(jié)束,將結(jié)果返回到A,B的調(diào)用幀才會消失。如果函數(shù)B內(nèi)部還調(diào)用函數(shù)C,那就還有一個C的調(diào)用幀,依次類推。所有的調(diào)用幀,就形成一個“調(diào)用?!?。

尾調(diào)用由于是函數(shù)的最后一步操作,所有不需要保留外層函數(shù)的調(diào)用幀,因?yàn)檎{(diào)用位置、內(nèi)部變量等信息都不會再用到了,只要直接用內(nèi)層函數(shù)的調(diào)用幀,取代外層函數(shù)的調(diào)用幀就可以了。

如果所有函數(shù)都是尾調(diào)用,那么完全可以做到每次執(zhí)行時,調(diào)用幀只有一項(xiàng),這將大大節(jié)省內(nèi)存。這就是“尾調(diào)用優(yōu)化”。注意,只有不再用到外層函數(shù)的內(nèi)部變量,內(nèi)層函數(shù)的調(diào)用幀才會取代外層函數(shù)的調(diào)用幀,否則就無法進(jìn)行“尾調(diào)用優(yōu)化”。

三、一個官網(wǎng)怎么樣優(yōu)化,有哪些指標(biāo),如何量化

3.1、數(shù)值量化

在前端開發(fā)中,此規(guī)則作為一種開發(fā)指導(dǎo)思路,針對瀏覽器頁面的性能優(yōu)化。

1、用戶在2秒內(nèi)得到響應(yīng),會感覺頁面的響應(yīng)速度很快 Fast
2、用戶在2~5秒間得到響應(yīng),會感覺頁面的響應(yīng)速度還行 Medium
3、用戶在5~8秒間得到響應(yīng),會感覺頁面的響應(yīng)速度很慢,但還可以接受 Slow
4、用戶在8秒后仍然無法得到響應(yīng),會感覺頁面的響應(yīng)速度垃圾死了。

對首屏加載速度影響最大的還是資源大小,請求數(shù)量,請求速度等。代碼方面,前端一般很難寫出嚴(yán)重影響速度的代碼。減小資源大小,可以用各種壓縮,懶加載,預(yù)加載,異步加載等方法。減少請求數(shù)量可以使用雪碧圖,搭建node中臺將多個請求合并成一個等。對于官網(wǎng)這種項(xiàng)目,最好使用服務(wù)端渲染,首屏快之外,也有利于SEO。

3.2、檢測方案

使用lighthouse進(jìn)行性能檢測,并對lighthouse提出的建議進(jìn)行優(yōu)化。

3.3、具體優(yōu)化方案

通過靜態(tài)化、圖片懶加載、圖片壓縮、異步加載(js和css)、優(yōu)化代碼等方式,以下是具體方法.

3.3.1、靜態(tài)化

服務(wù)端渲染,“直出”頁面,具有較好的SEO和首屏加載速度。主要還有以下的優(yōu)點(diǎn):
使用jsp模板語法(百度后發(fā)現(xiàn)是用Velocity模板語法)渲染頁面,減少了js文件體積,減少了請求數(shù)量,因?yàn)椴挥玫却罅拷涌诜祷?,加快了首屏?xí)r間
可以嘗試Vue的服務(wù)端渲染。首頁目前有部分是用接口讀取數(shù)據(jù),然后用jq進(jìn)行渲染,性能上應(yīng)該不如Virtual DOM,不過內(nèi)容不多。

3.3.2、圖片懶加載

這是一個很重要的優(yōu)化項(xiàng)。因?yàn)楣倬W(wǎng)上有很多圖片,而且編輯們上傳文章圖片的時候一般沒有壓縮,但是很多圖片的體積都很大。還有一個輪播圖,20張圖標(biāo),最小的幾十K,最大的兩百多K。對于圖片來源不可控的頁面,懶加載是個很實(shí)用的操作,直接將首屏加載的資源大小加少了十幾M。

3.3.3、圖片壓縮

對于來源可控,小圖標(biāo)等圖片可以用雪碧圖,base64等方法進(jìn)行優(yōu)化。目前只是用工具壓縮了圖片大小,后續(xù)可以考慮在webpack打包的時候生成雪碧圖。

3.3.4、異步加載js

通過標(biāo)簽引入的js文件,可以設(shè)置defer,async屬性讓其異步加載,而不會阻塞渲染。defer和async的區(qū)別在于async加載完就立即執(zhí)行,沒有考慮依賴,標(biāo)簽順序等。而defer加載完后會等它前面引入的文件執(zhí)行完再執(zhí)行。一般defer用的比較多,async只能用在那些跟別的文件沒有聯(lián)系的孤兒腳本上。

3.3.5、異步加載css

沒想到css也能異步加載,但這是lighthouse給出的建議。找了一下發(fā)現(xiàn)有以下兩種方法:
一是通過js腳本在文檔中插入標(biāo)簽
二是通過``的media屬性
media屬性是媒體查詢用的,用于在不同情況下加載不同的css。這里是將其設(shè)置為一個不適配當(dāng)前瀏覽器環(huán)境的值,甚至是不能識別的值,瀏覽器會認(rèn)為這個樣式文件優(yōu)先級低,會在不阻塞的情況加載。加載完成后再將media`設(shè)置為正常值,讓瀏覽器解析css。

<link rel="stylesheet"  media="none" onload="this.media='all'">

這里用的是第二種方法。但是webpack注入到html中的外鏈css還沒找到異步加載的方法。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容