不知不覺,春節就過完了,還沒來得及好好享受就沒了。好想來一場說走就走的旅行✈️,不吹水了,直接進入正題。javascript
最近在作一個需求,發現了薄弱的地方,趁這個好機會深刻了解一下,拓寬一下視野~java
衆所周知,網頁不只應該被快速加載,同時還應該流暢運行,好比快速響應的交互,如絲般順滑的動畫……node
在實際開發中如何作到上面所說的效果呢?web
第一個是 首屏呈現時間,網上的資料已經很是很是多了,壓縮代碼,使用webp圖片,使用sprite,按需加載,「直出」,CDN…… canvas
第二個是 16ms 優化,本篇重點講16ms的優化。瀏覽器
大多數設備的刷新頻率是60次/秒,(1000/60 = 16.6ms)也就說是瀏覽器對每一幀畫面的渲染工做要在16ms內完成,超出這個時間,頁面的渲染就會出現卡頓現象,影響用戶體驗。緩存
這就是上圖中的<16ms。瀏覽器在一幀裏面,會作如下這些動做。 固然,有些步驟(好比 layout,paint)是能夠省略的。閉包
若是改變屬性在上面圖中越往左,那麼影響就越大,效率就越低。dom
瀏覽器渲染的流程以下:ide
從上面圖中能夠看出,若是隻是改變composite(渲染層合併),那效率就會大大提升。
下面粗略地列出改變哪些樣式會分別改變渲染過程的哪一模塊。
從上圖能夠看到 transform,opacity 只會改變composite(渲染層合併),爲何呢?由於開啓了GPU加速。
小tips:先選中timeline
的某一幀,而後選擇下面的layer
標籤tab,能夠左右拖動該模塊出現3d
咱們能夠看到頁面上由以下層組成:
雖然咱們最終在瀏覽器上看到的只是一個複印版,即最終只有一個層。相似於PhotoShop軟件中的「圖層」概念,最後合併全部可視圖層,輸出一張圖片到屏幕上
可是實際上一個頁面會由於一些規則被分紅相應的層,一旦被獨立出來以後,便不會再影響其餘dom的佈局,由於它改變以後,只是「貼上」了頁面。
<video>
元素<canvas>
元素
說了這麼多瀏覽器渲染的原理,若是沒有尺子測量也毫無用處。那麼,下面就選尺子去丈量:谷歌開發工具的Timeline。
1. 點擊左上角的錄製以後,錄製結束後會生成下面的樣子,紅色區域內就是幀了,移動上去能夠看到每一幀的頻率,若是>60fps,就是比較流暢,若是<60fps,就會感到卡頓。
2. 在timeline下面,能夠看到各個模塊的耗時,能夠定位到耗時較大的函數上面,對該函數進行優化。
工具也有了,瀏覽器渲染的原理也知道了,接下來是結合實際項目進行優化.
結合上面的渲染流程圖,咱們能夠針對性的分析並優化下面的一些步驟
1. 讀寫分離,批量操做
JavaScript腳本運行的時候,它能獲取到的元素樣式屬性值都是上一幀畫面的,都是舊的值。
所以,若是你在當前幀獲取屬性以前又對元素節點有改動,那就會致使瀏覽器必須先應用屬性修改,結果執行佈局過程,最後再執行JavaScript邏輯。
// 先寫後讀,觸發強制佈局
function logBoxHeight() {
// 更新box樣式
box.classList.add('super-big'); // 爲了返回box的offersetHeight值 // 瀏覽器必須先應用屬性修改,接着執行佈局過程 console.log(box.offsetHeight); }
// 先讀後寫,避免強制佈局 function logBoxHeight() { // 獲取box.offsetHeight console.log(box.offsetHeight); // 更新box樣式 box.classList.add('super-big'); }
2. 閉包緩存計算結果 (須要頻繁的調用,計算的函數)
1 getMaxWidth: (function () {
2 var cache = {}; 3 function getwidth() { 4 if (maxWidth in cache) { 5 return cache[maxWidth]; 6 } 7 var target = this.node, 8 width = this.width, 9 screen = document.body.clientWidth, 10 num = target.length, 11 maxWidth = num * width + 10 * num + 20 - screen; 12 cache[maxWidth] = maxWidth; 13 return maxWidth; 14 } 15 return getwidth; 16 })(),
改爲這種方式後,直接蹭蹭蹭~ 減小了10多ms
而後在調用requestAnimationFrame的時候,若是你在一開始就作了讀取樣式屬性的操做,那麼將會觸發瀏覽器的強制同步佈局操做(即在javascript階段中執行佈局),這樣會致使屢次佈局,效率低下。
優化以下:
window.requestAnimationFrame(function () { context.animateTo(nowPos); //須要更新位置的交給RAF });
續上面,開啓paint flashing 以後,能夠看到瀏覽器從新繪製了哪些區域。發現有一些沒必要要重繪的區域也重繪了~給這些開啓GPU優化(上文中提到)
直接看 timeline 效果,全綠了~懸着的心終於放下了