日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
使用ChromeTimeline來(lái)優(yōu)化頁(yè)面性能

有時(shí)候,我們就是會(huì)不由自主地寫(xiě)出一些低效的代碼,嚴(yán)重影響頁(yè)面運(yùn)行的效率?;蛘呶覀兘邮值捻?xiàng)目中,前人寫(xiě)出來(lái)的代碼千奇百怪,比如為了一個(gè) Canvas 特效需要同時(shí)繪制 600 個(gè)三角形,又比如 Coding.net 的任務(wù)中心需要同時(shí) watch 上萬(wàn)個(gè)變量的變化等等。那么,如果我們遇到了一個(gè)比較低效的頁(yè)面,應(yīng)該如何去優(yōu)化它呢?

優(yōu)化前的準(zhǔn)備:知己知彼

在一切開(kāi)始之前,我們先打開(kāi) F12 面板,熟悉一下我們接下來(lái)要用到的工具:Timeline:

嗯沒(méi)錯(cuò)就是它。下面逐一介紹一下吧。區(qū)域 1 是一個(gè)縮略圖,可以看到除了時(shí)間軸以外被上下分成了四塊,分別代表 FPS、CPU 時(shí)間、網(wǎng)絡(luò)通信時(shí)間、堆棧占用;這個(gè)縮略圖可以橫向縮放,白色區(qū)域是下面可以看到的時(shí)間段(灰色當(dāng)然是不可見(jiàn)的啦)。區(qū)域 2 可以看一些交互事件,例如你滾動(dòng)了一下頁(yè)面,那么這里會(huì)出現(xiàn)一個(gè) scroll 的線段,線段覆蓋的范圍就是滾動(dòng)經(jīng)過(guò)的時(shí)間。區(qū)域 3 則是具體的事件列表了。

一開(kāi)始沒(méi)有記錄的時(shí)候,所有的區(qū)域都是空的。開(kāi)始統(tǒng)計(jì)和結(jié)束統(tǒng)計(jì)都很簡(jiǎn)單,左上角那坨黑色的圓圈就是。它右邊那個(gè)長(zhǎng)得像“禁止通行”的按鈕是用來(lái)清除現(xiàn)有記錄的。當(dāng)有數(shù)據(jù)的時(shí)候,我們把鼠標(biāo)滾輪向上滾,可以看到區(qū)域被放大了:

短短的時(shí)間里,瀏覽器做了這么多事情。對(duì)于一般的屏幕,原則上來(lái)說(shuō)一秒要往屏幕上繪制 60 幀,所以理論上講我們一幀內(nèi)的計(jì)算時(shí)間不能超過(guò) 16 毫秒,然而瀏覽器除了執(zhí)行我們的代碼以外,還要干點(diǎn)別的(例如計(jì)算 CSS,播放音頻……),所以其實(shí)我們能用的只有 10~12 毫秒左右。

差不多熟悉操作了,那么就來(lái)一下實(shí)戰(zhàn)吧!假如有一天,你接手了這樣一段代碼:

 
 
  1.  
  2.  
  3.  
  4.  
  5.      
  6.     Test 
  7.      
  8.  
  9.  
  10.     
 
  •     
     
  •     點(diǎn)我 
  •      
  •      
  •  
  •  
  •  
     
    1. // animation.js  
    2.   
    3. // 粒子總數(shù)  
    4. var COUNT = 500;  
    5. // 重力  
    6. var G = -0.1;  
    7. // 摩擦力  
    8. var F = -0.04;  
    9.   
    10. function init() {  
    11.     for (var i = 0; i < COUNT; i++) {  
    12.         var d = Math.random() * 2 * Math.PI;  
    13.         var v = Math.random() * 5;  
    14.         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;  
  •         // 計(jì)算新速度  
  •         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 計(jì)數(shù)器是 Chrome 調(diào)試工具自帶的):

    只有 10 FPS……10 FPS……坑爹呢這是!

    好吧,打開(kāi) Timeline,按下記錄按鈕,點(diǎn)一下頁(yè)面中的“點(diǎn)我”,稍微過(guò)一會(huì)兒停止記錄,就會(huì)得到一些數(shù)據(jù)。放大一些,對(duì) jQuery 比較熟悉的同學(xué)可以看出來(lái),這些大部分是 jQuery 的函數(shù)。我們點(diǎn)一下那個(gè) updateCircle 的區(qū)塊,然后看下面:

    這里告訴我們,這個(gè)函數(shù)運(yùn)行了多久、函數(shù)代碼在哪兒。我們點(diǎn)一下那個(gè)鏈接,于是就跳到了 Source 頁(yè):

    是不是很震撼,之前這個(gè)頁(yè)面只是用來(lái) Debug 的,沒(méi)想到現(xiàn)在居然帶了精確到行的運(yùn)行時(shí)間統(tǒng)計(jì)。當(dāng)然,這個(gè)時(shí)間是當(dāng)前這一行在“剛才我們點(diǎn)擊的區(qū)塊對(duì)應(yīng)的執(zhí)行時(shí)間段”中運(yùn)行的時(shí)間。所以我們就拿最慢的幾句話來(lái)下手吧!

    優(yōu)化一:減少 DOM 操作

    看到這幾行代碼,***反應(yīng)是:mdzz。本來(lái) DOM 操作就慢,還要在字符串和 float 之間轉(zhuǎn)來(lái)轉(zhuǎn)去。果斷改掉!于是用一個(gè)單獨(dú)的數(shù)組來(lái)存 x、y、d、v 這些屬性。

     
     
    1. var objects = []; 
    2. // 在 init 函數(shù)中 
    3. objects.push({ 
    4.     x: 250, 
    5.     y: 250, 
    6.     d: d, 
    7.     v: v 
    8. }); 
    9. // 在 updateCircle 函數(shù)中 
    10. var x = objects[i].x; 
    11. var y = objects[i].y; 
    12. var d = objects[i].d; 
    13. var v = objects[i].v; 
    14. // …. 
    15. objects[i].x = x; 
    16. objects[i].y = y; 
    17. objects[i].d = d; 
    18. objects[i].v = v; 

    效果顯著!我們?cè)賮?lái)看一下精確到行的數(shù)據(jù):

    優(yōu)化二:減少不必要的運(yùn)算

    所以最耗時(shí)的那句話已經(jīng)變成了計(jì)算 vx 和 vy,畢竟三角函數(shù)算法比較復(fù)雜嘛,可以理解。至于后面的三角函數(shù)為什么那么快,我猜可能是 Chrome 的 V8 引擎將其緩存了(這句話不保證正確性)。然而不知道大家有沒(méi)有發(fā)現(xiàn),其實(shí)計(jì)算 d 完全沒(méi)必要!我們只需要存 vx 和 vy 即可,不需要存 v 和 d!

     
     
    1. // init 
    2. var vx = v * Math.cos(d); 
    3. var vy = v * Math.sin(d); 
    4. objects.push({ 
    5.     x: 250, 
    6.     y: 250, 
    7.     vx: vx, 
    8.     vy: vy 
    9. }); 
    10. // updateCircle 
    11. var vx = objects[i].vx; 
    12. var vy = objects[i].vy; 
    13. // 計(jì)算新速度 
    14. var v = Math.sqrt(vx * vx + vy * vy); 
    15. if (Math.abs(vx) < 1e-9) vx = 0; 
    16. // 速度分量改變 
    17. vx += F * vx / v; 
    18. vy += F * vy / v + G; 
    19. // …. 
    20. objects[i].vx = vx; 
    21. objects[i].vy = vy; 

    只有加減乘除和開(kāi)平方運(yùn)算,每次比原來(lái)的時(shí)間又少了兩毫秒。從流暢的角度來(lái)說(shuō)其實(shí)已經(jīng)可以滿幀運(yùn)行了,然而為什么我還是覺(jué)得偶爾會(huì)有點(diǎn)卡呢?

    優(yōu)化三:替換 setInterval

    既然偶爾會(huì)掉幀,那么就看看是怎么掉的唄~原則上來(lái)說(shuō),在每一次瀏覽器進(jìn)行繪制之前,Timeline 里面應(yīng)該有一個(gè)叫 Paint 的事件,就像這樣:

    看到這些綠色的東西了沒(méi)?就是它們!看上面的時(shí)間軸,雖然代碼中 setInterval 的長(zhǎng)度是 1000/16 毫秒,但是其實(shí)根本不能保證!所以我們需要使用 requestAnimationFrame 來(lái)代替它。這是瀏覽器自帶的專(zhuān)門(mén)為動(dòng)畫(huà)服務(wù)的函數(shù),瀏覽器會(huì)自動(dòng)優(yōu)化這個(gè)函數(shù)的調(diào)用時(shí)機(jī)。并且如果頁(yè)面被隱藏,瀏覽器還會(huì)自動(dòng)暫停調(diào)用,有效地減少了 CPU 的開(kāi)銷(xiāo)。

     
     
    1. // 在 updateCircle ***加一句 
    2. requestAnimationFrame(updateCircle); 
    3. // 去掉全部跟 setInterval 有關(guān)的句子,把 showAnimation ***一句直接改成這個(gè) 
    4. updateCircle(); 

    我們至少可以保證,我們每算一次,屏幕上就會(huì)顯示一次,因此不會(huì)掉幀(前提是每計(jì)算一次的時(shí)間小于 12ms)。但是雖然計(jì)算時(shí)間少了,瀏覽器重計(jì)算樣式、繪制圖像的時(shí)間可是一點(diǎn)都沒(méi)變。能不能再做優(yōu)化呢?

    優(yōu)化四:使用硬件加速、避免反復(fù)查找元素

    如果我們用 transform 來(lái)代替 left 和 top 來(lái)對(duì)元素進(jìn)行定位,那么瀏覽器會(huì)為這個(gè)元素單***立一個(gè)合成層,專(zhuān)門(mén)使用 GPU 進(jìn)行渲染,這樣可以把重計(jì)算的代價(jià)降到***。有興趣的同學(xué)可以研究一下“CSS 硬件加速”的機(jī)制。同時(shí),我們可以緩存一下 jQuery 的元素(或者 DOM 元素),這樣不用每次都重新查找,也能稍微提高一點(diǎn)效率。如果把元素緩存在 objects 數(shù)組中,那么連 id 都不用寫(xiě)了!

     
     
    1. // init 
    2. var circle = $('
    '); 
  • objects.push({ 
  •     x: 250, 
  •     y: 250, 
  •     vx: vx, 
  •     vy: vy, 
  •     // 其實(shí)可以只存 DOM,不存 jQuery 對(duì)象 
  •     circle: circle[0] 
  • }); 
  • // updateCircle 里面 for 循環(huán)的***一句話替換掉 
  • objects[i].circle.style.transform = 'translate(' + x + 'px, ' + (400 - y) + 'px)';  
  • 看起來(lái)是不是很爽了?

    其實(shí),優(yōu)化是無(wú)止境的,例如我在 init 函數(shù)中完全可以不用 jQuery,改用 createDocumentFragment 來(lái)拼接元素,這樣初始化的時(shí)間就可以急劇縮短;調(diào)換 updateCircle 中的幾個(gè)語(yǔ)句的順序,在 V8 引擎下效率可能會(huì)有一定的提升;甚至還可以結(jié)合 Profile 面板來(lái)分析內(nèi)存占用,查看瀏覽器繪圖的細(xì)節(jié)……然而個(gè)人感覺(jué)并用不到這么極限的優(yōu)化。對(duì)于一個(gè)項(xiàng)目來(lái)說(shuō),如果單純?yōu)榱藘?yōu)化而寫(xiě)一些奇怪的代碼,是很不合算的。


    文章題目:使用ChromeTimeline來(lái)優(yōu)化頁(yè)面性能
    當(dāng)前路徑:http://www.5511xx.com/article/dhejggo.html