移動端最高頻耗內存的的操作 莫屬 touchmove 與scroll事件 兩者需要 微觀的 優化,使用 requestAnimationFrame性能優化 H5性能優化requestAnimationFrame
這里 我們 講述 touchmove;touchmove 事件發生很頻繁,會比屏幕刷新率快,導致無效的渲染和重繪;
幀數 –顯示設備通常的刷新率通常是50~60Hz –1000ms / 60 ≈ 16.6ms(1毫秒的優化意味着 6%的性能提升)
這就是 常說的 16.6毫秒的優化
瀏覽器對每一幀畫面的渲染工作需要在16毫秒(1秒 / 60 = 16.66毫秒)之內完成 ;如果超過了這個時間限度,頁面的渲染就會出現卡頓效果,也就是常說的jank;我們需要在 正確的時間 做正確的渲染;
拖拽的都會寫,先上圖看看效果;
大概了解一下 Timeline 查看看渲染情況 舊版如下( 新的 在 Performance )
ps Performance 可以在控制台出入 查看
(function() { handleAddListener('load', getTiming) function handleAddListener(type, fn) { if(window.addEventListener) { window.addEventListener(type, fn) } else { window.attachEvent('on' + type, fn) } } function getTiming() { try { var time = performance.timing; var timingObj = {}; var loadTime = (time.loadEventEnd - time.loadEventStart) / 1000; if(loadTime < 0) { setTimeout(function() { getTiming(); }, 200); return; } timingObj['重定向時間'] = (time.redirectEnd - time.redirectStart) / 1000; timingObj['DNS解析時間'] = (time.domainLookupEnd - time.domainLookupStart) / 1000; timingObj['TCP完成握手時間'] = (time.connectEnd - time.connectStart) / 1000; timingObj['HTTP請求響應完成時間'] = (time.responseEnd - time.requestStart) / 1000; timingObj['白屏時間'] = (time.responseStart - time.navigationStart) / 1000; timingObj['DOM渲染時間'] = (time.domComplete - time.domInteractive) / 1000; timingObj['domready時間--DOMContentLoaded事件完成的時間'] = (time.domContentLoadedEventEnd - time.fetchStart) / 1000; timingObj['onload時間 --頁面所有資源完全加載的時間 '] = (time.loadEventEnd-time.fetchStart)/1000; for(item in timingObj) { console.log(item + ":" + timingObj[item] + '毫秒(ms)'); } console.log(performance.timing); } catch(e) { console.log(timingObj) console.log(performance.timing); } } })();
上面的代碼 可以放入頁面查看性能。
在看看 幀模式 渲染情況;
那些沒必要的 move 什么也不需要做;沒必要在16.6毫秒內多余的event對象計算;
關於幀模式:
普通的拖拽
<script> function getStyle(obj,attr){ return obj.currentStyle? obj.currentStyle[attr]: getComputedStyle(obj,false)[attr]; } var oDiv = document.getElementById("oDiv"); //當前元素 var direction="horizontal"; var disX=0; var disY=0; var self = this; //上下文 var downLeft=0; var downTop=0; var isDown = false; var oDivWidth=parseInt(oDiv.offsetWidth); oDiv.onmousedown = function (e) { var e=e||window.event; //鼠標按下,計算當前元素距離可視區的距離 downLeft= parseInt(getStyle(oDiv,'left'));; downTop= parseInt(getStyle(oDiv,'top'));; disX = e.clientX ; disY = e.clientY; console.log("開始位置",e.clientX,"downLeft",downLeft); isDown = true; document.onmousemove = function (e) { var e=e||window.event; e.preventDefault(); oDiv.style.cursor="move"; if (isDown == false) { return; } //通過事件委托,計算移動的距離 var l = e.clientX - disX+downLeft; var t = e.clientY - disY+downTop; //移動當前元素 if(direction=="horizontal"){//水品 oDiv.style.left = l + 'px'; }else if(direction=="vertical"){//垂直 oDiv.style.top = t + 'px'; }else{ oDiv.style.left = l + 'px'; oDiv.style.top = t + 'px'; } // console.log("移動位置",e.clientX,"移動中left",l,"最終",getOffset(oDiv).left); //將此時的位置傳出去 //binding.value({x:l,y:t,direction:direction}) }; document.onmouseup = function (e) { var e=e||window.event; var left2=e.clientX-disX; var top2=e.clientY-disY; isDown = false; // console.log("結束位2置",e.pageX,"移asa中left",left2,"最終",getOffset(oDiv).left); //將此時的位置傳出去 document.onmousemove = null; document.onmouseup = null; return false; //FF等高版本瀏覽器中阻止默認行為 }; }; </script>
附上源代碼:
1 function drag(element){ 2 3 var startX=0, 4 startY=0, 5 ticking=false, 6 raf, 7 doc=document; 8 9 element.addEventListener("touchstart",function(e){ 10 11 12 var e=e||window.event, 13 touchs = e.touches[0]; 14 e.preventDefault(); //低端安卓 touch事件 有的導致touchend事件時效 必須開始 就加 e.preventDefault(); 15 // text a ipnut textarea 幾個 等標簽除外 16 // ,另外自定義移動端touchstart touchend組合的 hover事件,建議不加這個,不然頁面無法滾動 17 //touchmove 開始 就加 不然抖動一下,才能touchmove, 然后才正常 尤其早些的 三星 系列自帶瀏覽器 18 19 20 startX=parseInt(touchs.pageX-(element.lefts||0)); 21 startY=parseInt(touchs.pageY-(element.tops||0)); 22 23 doc.addEventListener("touchmove",update,false); 24 doc.addEventListener("touchend",end,false); 25 26 },false); 27 28 29 30 31 32 var update=function (e) { 33 34 var e=e||window.event; 35 if (e.touches.length > 1 || e.scale && e.scale !== 1) return; 36 e.preventDefault(); 37 38 //cancelAnimationFrame(raf); 39 if(!ticking) { 40 41 var touchs = e.changedTouches[0]; 42 43 //1先觸摸移動 44 element.lefts = touchs.pageX - startX; 45 element.tops = touchs.pageY - startY; 46 47 //2交給requestAnimationFrame 更新位置 48 //raf=requestAnimationFrame(function(){draw();}); 49 raf=requestAnimationFrame(draw); 50 51 } 52 53 ticking = true; 54 }; 55 56 57 58 59 var draw= function (){ 60 ticking = false; 61 var nowLeft=parseInt(element.lefts); //滑動的距離 touchmove時候,如果加阻力,可能有細小的抖動;我想應該是移動端 部分支持0.5px的緣故; parseInt的轉化有點牽強; 62 var nowTop=parseInt (element.tops); //滑動的距離 63 64 element.style.webkitTransform=element.style.transform = "translate3D(" + nowLeft + "px," + nowTop + "px,0px)"; 65 66 }; 67 68 var end=function(){ 69 var endLeft= parseInt(element.lefts); //滑動的距離 70 var endTop= parseInt(element.tops); //滑動的距離 71 72 //element.style.webkitTransform=element.style.transform = "translate(" + endLeft+ "px," + endTop + "px)"; 73 74 doc.removeEventListener("touchmove",update,false); 75 doc.removeEventListener("touchend",end,false); 76 // cancelAnimationFrame(raf); 77 78 } 79 80 }; 81
注意點:RequestAnimationFrame的兼容
;(function(){var lastTime=0;var vendors=["ms","moz","webkit","o"];for(var x=0;x<vendors.length&&!window.requestAnimationFrame;++x){window.requestAnimationFrame=window[vendors[x]+"RequestAnimationFrame"];window.cancelAnimationFrame=window[vendors[x]+"CancelAnimationFrame"]||window[vendors[x]+"CancelRequestAnimationFrame"]}if(!window.requestAnimationFrame){window.requestAnimationFrame=function(callback,element){var currTime=new Date().getTime();var timeToCall=Math.max(0,16-(currTime-lastTime));var id=window.setTimeout(function(){callback(currTime+timeToCall)},timeToCall);lastTime=currTime+timeToCall;return id}}if(!window.cancelAnimationFrame){window.cancelAnimationFrame=function(id){clearTimeout(id)}}}());
RequestAnimationFrame的簡易動畫庫
requestAnimationFrame 節流函數
function throttle1(fn, wait) { let previous = 0; return function () { let now = new Date().getTime(); if (now - previous > wait) { fn.apply(this, arguments); previous = now; } } } function raf_debounce(fn){ //touchmove scroll節流 var ticking=false; var that=this; return function(e) { var ev=e||window.event; // console.log("正1確的e",e); if(!ticking) { ticking = true; requestAnimationFrame(function(){ //console.log("正2確的e",ev); fn(ev); ticking = false; }); } } }; var sb=1; window.addEventListener("scroll",function(e){ if(sb){ sb=0; //var d = (+new Date()); console.log("sb",e) } }) var sa=1; window.addEventListener("scroll",throttle1(function(e){ if(sa){ //sa=0; var d = (+new Date()); console.log("sa",e); } },12)) var sc=1; window.addEventListener("scroll",raf_debounce(function(e){ if(sc){ //sa=0; var d = (+new Date()); console.log("sc",e); } }))
1513763671784
1513763671800
1513763671817
1513763671834
1513763671850
1513763671867
1513763671883
1513763671900
1513763671917
1513763671934
觀察最后2位 數字 后一個大於前一個的 16 兩個大約相差16.6毫秒 執行scroll fn一次
注意正確的上下文 event 對象
document.onmousemove = moveBy; // document.onmousemove = raf_debounce(function(e){ // moveBy (e); // });
充分合理 使用 requestAnimationFrame性能優化 對H5網頁的體驗 有着微觀細致的影響;
from memory cache與from disk cache
三級緩存原理
1、先查找內存,如果內存中存在,從內存中加載;
2、如果內存中未查找到,選擇硬盤獲取,如果硬盤中有,從硬盤中加載;
3、如果硬盤中未查找到,那就進行網絡請求;
4、加載到的資源緩存到硬盤和內存;
三、HTTP狀態碼及區別
-
200 form memory cache
不訪問服務器,一般已經加載過該資源且緩存在了內存當中,直接從內存中讀取緩存。瀏覽器關閉后,數據將不存在(資源被釋放掉了),再次打開相同的頁面時,不會出現from memory cache。 -
200 from disk cache
不訪問服務器,已經在之前的某個時間加載過該資源,直接從硬盤中讀取緩存,關閉瀏覽器后,數據依然存在,此資源不會隨着該頁面的關閉而釋放掉下次打開仍然會是from disk cache。 -
304 Not Modified
訪問服務器,發現數據沒有更新,服務器返回此狀態碼。然后從緩存中讀取數據。

鏈接:https://www.jianshu.com/p/8332da83955d
參考網站:
谷歌開發者,非常專業:https://developers.google.com/web/fundamentals/getting-started/?hl=zh-cn 需要翻牆;
Web前端性能優化的微觀分析 http://velocity.oreilly.com.cn/2013/ppts/16_ms_optimization--web_front-end_performance_optimization.pdf
移動端性能調優:ttps://speakerdeck.com/baofen14787/yi-dong-duan-xing-neng-diao-you-ji-16msyou-hua
總結:做的東西多了,得 整理一下以;
移動端 scroll,touchmove的事件用的還是比較多的;有時間還是要 細細優化的;雖然感覺很微觀 ,甚至覺得 優化的幾乎看不出來;但是你去優化好,還是費不少時間 的;
requestAnimationFrame是個移動端的利器;動畫盡量用它或者除集合css3實現;
一個基於 requestAnimationFrame 的動畫函數,仿造jquery http://www.cnblogs.com/surfaces/p/5129868.html