web前端頁面性能優化筆記

包括:資源的合併與壓縮,圖片編碼原理和類型選擇,瀏覽器的渲染機制,懶加載預加載,瀏覽器存儲,緩存機制,PWA,Vue-SSR等頁面優化的功能。

1.基礎優化:

     1.1 資源的合併與壓縮:

            1.1.1 理解減少http請求減少請求資源大小的優化:包括html壓縮,css壓縮,js壓縮,文件合併,開啓gzip。

                  ① html壓縮:包括空格,製表符,換行符等,以及HTML註釋都可以被壓縮。一般一個24KB的html文件壓縮之後,是22-23KB大小,看是html壓縮不大,但是以谷歌來說,如果1KB(即1024b字節)經過HTML壓縮減少一個字節,那麼每年也能替谷歌節約500TB流量,約近億元的節約量。

                        如何進行html壓縮?:包括在線網站壓縮(如http://tool.oschina.net),nodejs提供了html-minfier工具,後端模塊引擎渲染壓縮。目前很多框架的構建工具,如vue-cli,webpack等都已經在打包項目代碼的時候進行了html壓縮。

                  ② css壓縮:包括刪除無效css代碼,如註釋,合併相同css代碼等。

                        如何進行css壓縮?:包括在線網站壓縮(如http://tool.oschina.net),nodejs提供了html-minfier工具,使用clean-css對css進行壓縮。目前很多框架的構建工具,如vue-cli,webpack等都已經在打包項目代碼的時候進行了css壓縮。

                  ③ js壓縮:包括刪除無效代碼/字符,剔除註釋,代碼語義的縮減和優化,代碼保護(讓人不容易一眼看清晰代碼)。

                        如何進行js壓縮?:包括在線網站壓縮(如http://tool.oschina.net),nodejs提供了html-minfier工具,使用uglifyjs2對js進行壓縮。目前很多框架的構建工具,如vue-cli,webpack等都已經在打包項目代碼的時候進行了js壓縮。

                  ④ 文件合併:即將所要的請求,合併到一個文件,一起發送給客戶端。不合並文件時,請求一次,返回一次,存在多次請求,多次返回,就會有網絡延遲,網絡故障.....等問題。合併文件時,將所有請求合併到一個文件,然後在一次性發送給後端,減少了網絡請求的的次數以及頻繁請求帶來的問題,但是也會存在首屏渲染問題(因爲是合併到一個文件裏所有該文件較大,在第一次請求時,會存在較慢,如果瀏覽器需要根據請求返回的數據才進行渲染,即存在首屏加載渲染頁面比較慢),以及緩存失效的問題(即多個文件合併到一個文件,如果其中一個文件發送細微的改變,那麼合併後的文件都需要重新獲取請求,即原合併後的緩存失效,解決:公共庫的合併,不同頁面的合併(單頁面的合併))。

                        如何進行文件合併?:包括在線網站文件合併(如http://tool.oschina.net),nodejs實現合併。目前很多框架的構建工具,如vue-cli,webpack等都已經在打包項目代碼的時候進行了文件合併。

            1.1.2 掌握壓縮與合併的原理:

            1.1.3 通過在線網站和fis3兩種方式實現優化:

                  ① fis3:是百度的構建化的工具,是基於前端和後端一起的構建化工具(相比wepack更完整)。

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

文字解析:用戶在瀏覽器中輸入一個url,瀏覽器將url拆分解析,然後將解析後的domain發送到dns服務器上,dns服務器根據domain查詢相關的ip地址,然後將ip地址返回給瀏覽器,瀏覽器然後將ip以及相關參數通過網絡(局域網,路由器,主幹網)發送到服務端,服務端有MVC架構,服務端通過model,redis+db返回view數據視圖,最後返回render,在瀏覽器頁面進行渲染html,css,html.....。

請求過程中潛在的性能優化點:dns是否可以通過緩存減少dns查詢時間?網絡請求過程中走最近的網絡環境?相同的靜態資源是否可以緩存?能否減少http請求大小?減少http請求次數?改爲服務端渲染?

     1.3 圖片優化:

          1.3.1:主要包括如下圖片

               ① 有損壓縮:JPG格式的圖片壓縮,但是損失的部分,在肉眼上視覺上沒有多大影響,壓縮率較高,不支持透明。

               ② 無損壓縮:PNG格式的圖片壓縮,支持透明,瀏覽器兼容好,存在降階。

               ③ webp壓縮:webp壓縮程度更好,但在ios webview上有兼容問題,在安卓上很好的支持。

               ④ svg矢量圖:通過HTML標籤代碼內嵌,相對較小,適用於圖片樣式相對簡單的場景,如繪製logo。

               ⑤ 使用iconfont圖標替代圖片:通過使用iconfont替代圖片,iconfont是用svg繪製。

          1.3.2:在線壓縮圖片網站:https://tinypng.com/

          1.3.3:在線圖片格式轉換:即png、webp相互轉換,如https://zhitu.isux.us/。也可通過fis3插件配置在代碼中,實現png、jpg、webp圖片的格式的相互轉換。

          1.3.4:inline-image:即圖片內嵌,將圖片轉換爲 Data url base64格式,參考https://blog.csdn.net/u014465934/article/details/83750121。fis3中存在封裝的__inline('img/1.webp')方法,可以直接將'img/1.webp'圖片轉換爲圖片內嵌。圖片內嵌的特點:

            ① Data URI  替換 img src :減少外部資源Http請求,

            ② 使用 css background-image: url("data:image/gif;base64,R0lGODlhAwAD): 實現緩存

            ③ Data URI 轉換後的圖片的體積偏大 不適用於大圖:適用於圖片小於4M的圖片。

#在fis3中
var inlineImage=__inline('img/1.webp');
<img src="'+inlineImage+'">

           1.3.5:雪碧圖:雪碧圖css位置生成網站www.spritecow.com。將多張圖片通過photoshop合併到一張圖片上,可以減少http請求,但是會增加該圖片的大小。


拓展:

     png8/png24/png/32之間的區別:圖片的大小,以及圖片色彩的豐富度。

          png8——256色+支持透明,大小較小。

          png24——2^24色+不支持透明

          png32——2^24色+支持透明


2.進階優化:一個網站在瀏覽器端是如何進行渲染的?

     2.1 html渲染的特點:

          2.1.1:順序執行,併發加載:html代碼解析的順序是從上到下執行的,即html是順序執行的,html資源是併發請求的,在某個域名下,併發請求數是有上限的。

          2.1.2:是否阻塞:css的加載是否阻塞後續Js的加載和執行,以及頁面的渲染。js的加載是否阻塞後續Js的加載和執行.....,推薦css樣式在html的head標籤中通過link引入,在css加載完之前,後面的js的執行是會被阻塞的,css不阻塞外部腳本的加載(因爲外部加載可以是異步加載,預加載)但是可能會阻塞執行。直接通過srcript引入js會阻塞頁面的渲染(因爲js可能會動態修改dom結構),js不阻塞資源的加載,js順序執行,阻塞後續的js邏輯的執行。

          2.1.3:依賴關係:HTML在渲染過程中依賴關係。如頁面已經渲染出來,但是css還沒加載完成,或css加載很慢,之後突然閃爍一下頁面,然後css才加載完成並渲染到頁面了,這是沒有遵循好依賴關係。有比如js的執行是否存在依賴,a.js執行依賴b.js文件,如果a.js先加載完,但是b.js還沒有加載,就會存在問題。

          2.1.4:引入方式:動態異步的在某個時間點觸發js的引入加載。

     2.2 懶加載和預加載:

          2.2.1:懶加載:即延遲加載資源,減少無效資源的加載,如電商類圖片很多,頁面很長,若沒有進入用戶可視區的圖片,就暫時不用加載,進入用戶可視區的圖片才加載。包括原始js實現懶加載和第三方庫zepto.lazyload.js實現懶加載

<!--
懶加載的原理:當圖片出現在可視區內,就開始請求加載圖片了。通過監聽頁面scroll,判斷圖片是否進入可視區,如果進入就開始懶加載該圖片了
注意:1.圖片一定要有固體的寬高尺寸大小,才能懶加載。
     2.初始化時,圖片的src爲空字符串。
     3.圖片的url初始化要存在自定義屬性中,如data-original。
-->
<!DOCTYPE html>
<html>
<head>        
    <meta charset="UTF-8">
    <title>1. 原生js實現懶加載</title>
    <style type="text/css">
    	.img-lists{
    		margin-left: 5%;
    	}
    	.pic{
    		width: 40%;
    		height: 400px;
    		margin-right: 5%;
    		display: inline-block;
    		height: 400px;
    		background: grey;
    	}
    </style>
</head>
<body>
	<div class="img-lists">
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
	</div>
    <script>    
       const heightVal=document.documentElement.clientHeight;  //獲取可視區的高,即頁面body的高。
       function lazyload(){
       	let ele=document.querySelectorAll('img[data-original][lazyload]');  //查找頁面所有有[data-original][lazyload]這2個屬性的圖片標籤。
       	Array.prototype.forEach.call(ele,function(item,index){
       		let obj; //聲明變量用於存放當前圖片的寬度尺寸數據
       		if(item.dataset.original ===''){
       			return
       		}else{
       			obj=item.getBoundingClientRect();  //獲取當前圖片的寬度尺寸數據
       		}
       		if(obj.bottom>=0 && obj.top<heightVal){ //如果當前圖片在可視區之類
       			function(){
       				let img=new Image();
       				img.src=item.dataset.original;
       				img.οnlοad=function(){
       					item.src=img.src;
       				}
       				item.removeAttribute('data-original')
       				item.removeAttribute('lazyload')
       			}()
       		}
       	})
       }
        lazyload()
        window.addEventListener('scroll',lazyload)
    </script>
</body>
</html>
<!--
懶加載的原理:當圖片出現在可視區內,就開始請求加載圖片了。通過監聽頁面scroll,判斷圖片是否進入可視區,如果進入就開始懶加載該圖片了
注意:1.圖片一定要有固體的寬高尺寸大小,才能懶加載。
     2.初始化時,圖片的src爲空字符串。
     3.圖片的url初始化要存在自定義屬性中,如data-original。
-->
<!DOCTYPE html>
<html>
<head>        
    <meta charset="UTF-8">
    <title>2.zepto.lazyload.js實現懶加載</title>
    <style type="text/css">
    	.img-lists{
    		margin-left: 5%;
    	}
    	.pic{
    		width: 40%;
    		height: 400px;
    		margin-right: 5%;
    		display: inline-block;
    		height: 400px;
    		background: grey;
    	}
    </style>
</head>
<body>
	<div class="img-lists">
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
		<img src=""  class="pic" data-original='http://pic31.nipic.com/20130801/11604791_100539834000_2.jpg' lazyload='true'/>
	</div>
   <script src='./zepto.lazyload.js'></script>
   <script src='./zepto.min.js'></script>
   <script>
        $('img[data-original][lazyload]').lazyload()
   </script>
</body>
</html>

          2.2.2:預加載:即提前加載資源,然後在緩存中就可以直接使用資源,如H5項目中,提前加載好圖片資源,以免切換動畫效果時,出現空白情況。四種預加載的方式:

              ① 直接在img標籤的src中加載:先隱藏該圖片,需要的時候再顯示,即可從緩存中讀取該圖片

<!--方式1:直接在img標籤的src中加載,先隱藏該圖片,需要的時候再顯示,即可從緩存中讀取該圖片-->
<img src="http://pic41.nipic.com/20140508/18609517_112216473140_2.jpg" style='display:none'/>

              ② 使用Image對象加載:使用new Image() 事先加載好圖片,在需要的時候,從緩存中獲取。

//方式二:使用new Image() 事先加載好圖片,在需要的時候,從緩存中獲取
var img=new Image()
img.src="http://pic41.nipic.com/20140508/18609517_112216473140_2.jpg" 


document.getElementById('myimage').src=img.src;  //預先加載好圖片,在需要的時候,從緩存中獲取

               ③ XMLHttpRequest:可以更好處理圖片請求的整個過程,但是存在跨域的問題。

var xml=new XMLHttpRequest();
xml.onreadystatechange=callback;
xml.οnprοgress=progressCallback;
xml.open('GET','http://pic41.nipic.com/20140508/18609517_112216473140_2.jpg',True);
xml.send();

function callback(){
    if(xml.readState==4 && xml.status==200){
        var text=xml.responseText;       
    }else{
        console.log('請求失敗')
    }
}
function progressCallback(e){
    console.log(e)
    console.log('查看預加載的過程')
}

               ④ 使用第三方庫PreloadJS實現預加載:

<script src='./preload.min.js'></script>
<script>
    var queue=new createjs.LoadQueue(false);   //參數爲true時(類似xmlHttpRequest加載),存在跨域問題,爲false時類似標籤加載(img/new Image)。
    queue.on('complete',handleComplete,this);
    queue.loadManifest([
        {
            id:'myImage1',src='http://img.redocn.com/sheying/20150213/mulanweichangcaoyuanfengjing_3951976.jpg'
        },{
            id:'myImage2',src='http://img.redocn.com/sheying/20150213/mulanweichangcaoyuanfengjing_3951976.jpg'
        }
    ])

    function handleComplete(){
        var image=queue.getRequest('myImage1');
        document.body.appendChild(image);
    }
</script>

   

     2.3 瀏覽器的重繪與迴流:

          2.3.1 css性能讓js變慢?一個線程執行ui渲染,一個線程執行js,原本是2個分開的線程,理論上不應該相互阻塞,但是js可能獲取或修改ui渲染的結果,就使得瀏覽器在執行js時,會凍結ui線程,在執行ui渲染時,會凍結js線程執行,從而形成阻塞。頻繁的觸發重繪與迴流,會導致UI頻繁渲染,最終導致js變慢。

          2.3.2 什麼是迴流:當render tree(dom樹)中的一部分(或全部)因爲元素的大小尺寸,佈局位置,隱藏顯示等改變而需要重新構建,即爲迴流。當頁面佈局和幾何屬性改變時,都會觸發迴流。一些元素的css會觸發迴流,如:盒子模型相關屬性,定位屬性及浮動,改變文字結構。

          2.3.3 什麼是重繪:當render tree(dom樹)中的一部分(或全部)元素的屬性更新改變,從而影響元素的外觀,風格,而不會影響大小布局的,即爲重繪。一些元素的css會觸發重繪,如背景顏色,字體顏色,內外邊框,邊框陰影等。

          2.3.4 解決重繪迴流帶來的性能問題方案:

              ① 避免使用觸發重繪迴流的css屬性。
              ② 將重繪迴流的影響範圍限制在單獨的圖層內,video和canvas會自動創建圖層。

              ③ 使用translate代替top:因爲top/left/bottom/right.....的值變動會改變元素佈局,引發迴流重繪,而translate不會觸發迴流(即不會觸發layout迴流過程),只會觸發重繪(paint)。
              ④ 使用opcaity替代visibility:因爲visibility只會觸發重繪,而opcaity不會(前提示該元素是單獨獨立出來的圖層如添加transform:translateZ(0),或添加will-change:transform變成了圖層) 。

              ⑤ 多使用class替代一天一天修改dom樣式:因爲每修改一條dom樣式,會觸發一次重繪。
              ⑥ 讓dom離線後修改:如先把dom樣式改爲display:none(會觸發一次迴流),之後可以修改n次dom樣式,不會觸發迴流重繪,再最後dom樣式全部修改完在讓其顯示。

              ⑦ 不要讓dom節點屬性值放在一個循環裏當成循環裏的變量:如循環裏獲取offsetHeight/offsetWidth,會刷新緩存區,獲取最新的dom的offsetHeight/offsetWidth,會觸發迴流,因爲迴流也有緩存機制。

              ⑧ 儘量不用table,可以用div替代:因爲一個很小的改動會觸發table重新佈局,即觸發迴流重繪。

              ⑨ 動畫的速度:動畫效果會觸發迴流重繪,所以需要選擇合適的動畫速度。

              ⑩ 新增圖層:圖層裏面的元素改變,只會觸發當前圖層的迴流重繪,不會影響其他圖層,chorme會根據性能自動新增圖層。

              ⑾ 啓動GPU硬件加速:合適的GPU加速,不易過多。

 

 


拓展:

     1、迴流必定引起重繪,重繪不一定引起迴流。

     2、將普通元素變成圖層的方式:儘量在需要時候,添加圖層,頁面大量的圖層會影響性能,因爲圖層合成上會花費很多時間。

           1.1 添加transform:translateZ(0)。

           1.2 添加will-change:transform。


 

 

 

3.結合服務端優化:

4.回顧總結:

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章