摘要:區(qū)域則是具體的事件列表了。果斷改掉于是用一個多帶帶的數(shù)組來存這些屬性。并且如果頁面被隱藏,瀏覽器還會自動暫停調(diào)用,有效地減少了的開銷。對于一個項目來說,如果單純?yōu)榱藘?yōu)化而寫一些奇怪的代碼,是很不合算的。
有時候,我們就是會不由自主地寫出一些低效的代碼,嚴重影響頁面運行的效率。或者我們接手的項目中,前人寫出來的代碼千奇百怪,比如為了一個 Canvas 特效需要同時繪制 600 個三角形,又比如 Coding.net 的任務(wù)中心需要同時 watch 上萬個變量的變化等等。那么,如果我們遇到了一個比較低效的頁面,應該如何去優(yōu)化它呢?
優(yōu)化前的準備:知己知彼在一切開始之前,我們先打開 F12 面板,熟悉一下我們接下來要用到的工具:Timeline:
嗯沒錯就是它。下面逐一介紹一下吧。區(qū)域 1 是一個縮略圖,可以看到除了時間軸以外被上下分成了四塊,分別代表 FPS、CPU 時間、網(wǎng)絡(luò)通信時間、堆棧占用;這個縮略圖可以橫向縮放,白色區(qū)域是下面可以看到的時間段(灰色當然是不可見的啦)。區(qū)域 2 可以看一些交互事件,例如你滾動了一下頁面,那么這里會出現(xiàn)一個 scroll 的線段,線段覆蓋的范圍就是滾動經(jīng)過的時間。區(qū)域 3 則是具體的事件列表了。
一開始沒有記錄的時候,所有的區(qū)域都是空的。開始統(tǒng)計和結(jié)束統(tǒng)計都很簡單,左上角那坨黑色的圓圈就是。它右邊那個長得像“禁止通行”的按鈕是用來清除現(xiàn)有記錄的。當有數(shù)據(jù)的時候,我們把鼠標滾輪向上滾,可以看到區(qū)域被放大了:
短短的時間里,瀏覽器做了這么多事情。對于一般的屏幕,原則上來說一秒要往屏幕上繪制 60 幀,所以理論上講我們一幀內(nèi)的計算時間不能超過 16 毫秒,然而瀏覽器除了執(zhí)行我們的代碼以外,還要干點別的(例如計算 CSS,播放音頻……),所以其實我們能用的只有 10~12 毫秒左右。
差不多熟悉操作了,那么就來一下實戰(zhàn)吧!假如有一天,你接手了這樣一段代碼:
Test
// animation.js // 粒子總數(shù) var COUNT = 500; // 重力 var G = -0.1; // 摩擦力 var F = -0.04; function init() { for (var i = 0; i < COUNT; i++) { var d = Math.random() * 2 * Math.PI; var v = Math.random() * 5; var circle = $(""); circle.appendTo($(".main")); } } function updateCircle() { for (var i = 0; i < COUNT; i++) { var x = parseFloat($("#circle-" + i).attr("data-x")); var y = parseFloat($("#circle-" + i).attr("data-y")); var d = parseFloat($("#circle-" + i).attr("data-d")); var v = parseFloat($("#circle-" + i).attr("data-v")); var vx = v * Math.cos(d); var vy = v * Math.sin(d); if (Math.abs(vx) < 1e-9) vx = 0; // 速度分量改變 vx += F * Math.cos(d); vy += F * Math.sin(d) + G; // 計算新速度 v = Math.sqrt(vx * vx + vy * vy); if (vy > 0) d = Math.acos(vx / v); else d = -Math.acos(vx / v); // 位移分量改變 x += vx; y += vy; $("#circle-" + i).attr("data-x", x); $("#circle-" + i).attr("data-y", y); $("#circle-" + i).attr("data-d", d); $("#circle-" + i).attr("data-v", v); $("#circle-" + i).css({"top": 400 - y, "left": x}); } } var interval = null; function showAnimation() { if (interval) clearInterval(interval); $(".main").html(""); init(); interval = setInterval(updateCircle, 1000 / 60); }
效果如下(右上角的 FPS 計數(shù)器是 Chrome 調(diào)試工具自帶的):
只有 10 FPS……10 FPS……坑爹呢這是!
好吧,打開 Timeline,按下記錄按鈕,點一下頁面中的“點我”,稍微過一會兒停止記錄,就會得到一些數(shù)據(jù)。放大一些,對 jQuery 比較熟悉的同學可以看出來,這些大部分是 jQuery 的函數(shù)。我們點一下那個 updateCircle 的區(qū)塊,然后看下面:
這里告訴我們,這個函數(shù)運行了多久、函數(shù)代碼在哪兒。我們點一下那個鏈接,于是就跳到了 Source 頁:
是不是很震撼,之前這個頁面只是用來 Debug 的,沒想到現(xiàn)在居然帶了精確到行的運行時間統(tǒng)計。當然,這個時間是當前這一行在“剛才我們點擊的區(qū)塊對應的執(zhí)行時間段”中運行的時間。所以我們就拿最慢的幾句話來下手吧!
優(yōu)化一:減少 DOM 操作看到這幾行代碼,第一反應是:mdzz。本來 DOM 操作就慢,還要在字符串和 float 之間轉(zhuǎn)來轉(zhuǎn)去。果斷改掉!于是用一個多帶帶的數(shù)組來存 x、y、d、v 這些屬性。
var objects = []; // 在 init 函數(shù)中 objects.push({ x: 250, y: 250, d: d, v: v }); // 在 updateCircle 函數(shù)中 var x = objects[i].x; var y = objects[i].y; var d = objects[i].d; var v = objects[i].v; // …. objects[i].x = x; objects[i].y = y; objects[i].d = d; objects[i].v = v;
效果顯著!我們再來看一下精確到行的數(shù)據(jù):
優(yōu)化二:減少不必要的運算所以最耗時的那句話已經(jīng)變成了計算 vx 和 vy,畢竟三角函數(shù)算法比較復雜嘛,可以理解。至于后面的三角函數(shù)為什么那么快,我猜可能是 Chrome 的 V8 引擎將其緩存了(這句話不保證正確性)。然而不知道大家有沒有發(fā)現(xiàn),其實計算 d 完全沒必要!我們只需要存 vx 和 vy 即可,不需要存 v 和 d!
// init var vx = v * Math.cos(d); var vy = v * Math.sin(d); objects.push({ x: 250, y: 250, vx: vx, vy: vy }); // updateCircle var vx = objects[i].vx; var vy = objects[i].vy; // 計算新速度 var v = Math.sqrt(vx * vx + vy * vy); if (Math.abs(vx) < 1e-9) vx = 0; // 速度分量改變 vx += F * vx / v; vy += F * vy / v + G; // …. objects[i].vx = vx; objects[i].vy = vy;
只有加減乘除和開平方運算,每次比原來的時間又少了兩毫秒。從流暢的角度來說其實已經(jīng)可以滿幀運行了,然而為什么我還是覺得偶爾會有點卡呢?
優(yōu)化三:替換 setInterval既然偶爾會掉幀,那么就看看是怎么掉的唄~原則上來說,在每一次瀏覽器進行繪制之前,Timeline 里面應該有一個叫 Paint 的事件,就像這樣:
看到這些綠色的東西了沒?就是它們!看上面的時間軸,雖然代碼中 setInterval 的長度是 1000/16 毫秒,但是其實根本不能保證!所以我們需要使用 requestAnimationFrame 來代替它。這是瀏覽器自帶的專門為動畫服務(wù)的函數(shù),瀏覽器會自動優(yōu)化這個函數(shù)的調(diào)用時機。并且如果頁面被隱藏,瀏覽器還會自動暫停調(diào)用,有效地減少了 CPU 的開銷。
// 在 updateCircle 最后加一句 requestAnimationFrame(updateCircle); // 去掉全部跟 setInterval 有關(guān)的句子,把 showAnimation 最后一句直接改成這個 updateCircle();
我們至少可以保證,我們每算一次,屏幕上就會顯示一次,因此不會掉幀(前提是每計算一次的時間小于 12ms)。但是雖然計算時間少了,瀏覽器重計算樣式、繪制圖像的時間可是一點都沒變。能不能再做優(yōu)化呢?
優(yōu)化四:使用硬件加速、避免反復查找元素如果我們用 transform 來代替 left 和 top 來對元素進行定位,那么瀏覽器會為這個元素多帶帶創(chuàng)立一個合成層,專門使用 GPU 進行渲染,這樣可以把重計算的代價降到最低。有興趣的同學可以研究一下“CSS 硬件加速”的機制。同時,我們可以緩存一下 jQuery 的元素(或者 DOM 元素),這樣不用每次都重新查找,也能稍微提高一點效率。如果把元素緩存在 objects 數(shù)組中,那么連 id 都不用寫了!
// init var circle = $(""); objects.push({ x: 250, y: 250, vx: vx, vy: vy, // 其實可以只存 DOM,不存 jQuery 對象 circle: circle[0] }); // updateCircle 里面 for 循環(huán)的最后一句話替換掉 objects[i].circle.style.transform = "translate(" + x + "px, " + (400 - y) + "px)";
看起來是不是很爽了?
其實,優(yōu)化是無止境的,例如我在 init 函數(shù)中完全可以不用 jQuery,改用 createDocumentFragment 來拼接元素,這樣初始化的時間就可以急劇縮短;調(diào)換 updateCircle 中的幾個語句的順序,在 V8 引擎下效率可能會有一定的提升;甚至還可以結(jié)合 Profile 面板來分析內(nèi)存占用,查看瀏覽器繪圖的細節(jié)……然而個人感覺并用不到這么極限的優(yōu)化。對于一個項目來說,如果單純?yōu)榱藘?yōu)化而寫一些奇怪的代碼,是很不合算的。
—
P.S. 全部的代碼在這里,歡迎吐槽:
未優(yōu)化版 | 優(yōu)化版
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/80045.html
摘要:如果網(wǎng)頁動畫能夠做到每秒幀,就會跟顯示器同步刷新,達到最佳的視覺效果。下面的一條是,低于這條線,可以達到每秒幀上面的一條是,低于這條線,可以達到每秒次渲染。圖中幀柱的高度表示了該幀的總耗時,幀柱中的顏色分別對應該幀中包含的不停類型的事件。 原文地址:http://horve.github.io/2015/10/26/timeli... 隨著webpage可以承載的表現(xiàn)形式更加多樣化,通...
摘要:頁面性能優(yōu)化學而不思則惘,思而不學則殆前幾天接到一個頁面效果優(yōu)化的任務(wù),邊做邊查閱了一些關(guān)于頁面性能的資料。可能只需要在中使用這類屬性,即可開啟硬件加速硬件加速真的那么好嗎從本人在移動端開發(fā)的實踐來看,硬件加速是比較坑的。 頁面性能優(yōu)化 學而不思則惘,思而不學則殆 前幾天接到一個頁面效果優(yōu)化的任務(wù),邊做邊查閱了一些關(guān)于頁面性能的資料。做完任務(wù)之后,抽空寫了一篇總結(jié),梳理一下思路,加深自...
摘要:頁面性能優(yōu)化學而不思則惘,思而不學則殆前幾天接到一個頁面效果優(yōu)化的任務(wù),邊做邊查閱了一些關(guān)于頁面性能的資料。可能只需要在中使用這類屬性,即可開啟硬件加速硬件加速真的那么好嗎從本人在移動端開發(fā)的實踐來看,硬件加速是比較坑的。 頁面性能優(yōu)化 學而不思則惘,思而不學則殆 前幾天接到一個頁面效果優(yōu)化的任務(wù),邊做邊查閱了一些關(guān)于頁面性能的資料。做完任務(wù)之后,抽空寫了一篇總結(jié),梳理一下思路,加深自...
摘要:在這里介紹移動中嵌入網(wǎng)頁,優(yōu)化頁面顯示速度。測試代碼如果后面找到解決方案,會更新到后面。一次移動優(yōu)化之旅二參考使用的分析頁面性能技術(shù)筆記詳解網(wǎng)頁渲染過程 在這里介紹移動App中嵌入網(wǎng)頁,優(yōu)化頁面顯示速度。 前言 最近在Boss的要求下,寫組件模式的移動頁面。這里選擇的庫是react。(為什么? 入門簡單 + JSX)。 在經(jīng)過愉快的編寫組件后,打包組件形成組件庫文件,然后被頁面引用。配...
閱讀 1074·2021-11-24 09:39
閱讀 1306·2021-11-18 13:18
閱讀 2425·2021-11-15 11:38
閱讀 1824·2021-09-26 09:47
閱讀 1625·2021-09-22 15:09
閱讀 1623·2021-09-03 10:29
閱讀 1510·2019-08-29 17:28
閱讀 2950·2019-08-29 16:30