亚洲一区二区观看播放-疯狂做受xxxx高潮欧美日本-国产极品粉嫩泬免费观看-暖暖www视频免费高清最新期

Web前端知識(shí)

首頁(yè) > 免費(fèi) > Web前端知識(shí) >

前端性能——高性能滾動(dòng) scroll 及頁(yè)面渲染優(yōu)化(一)

來(lái)源:北京匯仁智杰科技有限公司   時(shí)間:2016-05-18   點(diǎn)擊: 次

  最近在研究頁(yè)面渲染及web動(dòng)畫(huà)的性能問(wèn)題,以及拜讀《CSS SECRET》(CSS揭秘)這本大作。
  本文主要想談?wù)勴?yè)面優(yōu)化之滾動(dòng)優(yōu)化。
  主要內(nèi)容包括了為何需要優(yōu)化滾動(dòng)事件,滾動(dòng)與頁(yè)面渲染的關(guān)系,節(jié)流與防抖,pointer-events:none 優(yōu)化滾動(dòng)。因?yàn)楸疚纳婕傲撕芏嗪芏嗷A(chǔ),是我自己學(xué)習(xí)記錄的一個(gè)過(guò)程,如果上面列出的知識(shí)點(diǎn)都了然于胸了,就可以不必往下看了。
  滾動(dòng)優(yōu)化的由來(lái)
  滾動(dòng)優(yōu)化其實(shí)也不僅僅指滾動(dòng)(scroll 事件),還包括了例如 resize 這類(lèi)會(huì)頻繁觸發(fā)的事件。簡(jiǎn)單的看看:
var i = 0;
window.addEventListener('scroll',function(){
 console.log(i++);
},false);
  在綁定 scroll 、resize這類(lèi)事件時(shí),當(dāng)它發(fā)生時(shí),它被觸發(fā)的頻次非常高,間隔很近。如果事件中涉及到大量的位置計(jì)算、DOM 操作、元素重繪等工作且這些工作無(wú)法在下一個(gè) scroll 事件觸發(fā)前完成,就會(huì)造成瀏覽器掉幀。加之用戶(hù)鼠標(biāo)滾動(dòng)往往是連續(xù)的,就會(huì)持續(xù)觸發(fā) scroll 事件導(dǎo)致掉幀擴(kuò)大、瀏覽器CPU使用率增加、用戶(hù)體驗(yàn)受到影響。
  在滾動(dòng)事件中綁定回調(diào)應(yīng)用場(chǎng)景也非常多,在圖片的懶加載、下滑自動(dòng)加載數(shù)據(jù)、側(cè)邊浮動(dòng)導(dǎo)航欄等中有著廣泛的應(yīng)用。
  當(dāng)用戶(hù)瀏覽網(wǎng)頁(yè)時(shí),擁有平滑滾動(dòng)經(jīng)常是被忽視但卻是用戶(hù)體驗(yàn)中至關(guān)重要的部分。當(dāng)滾動(dòng)表現(xiàn)正常時(shí),用戶(hù)就會(huì)感覺(jué)應(yīng)用十分流暢,令人愉悅,反之,笨重不自然卡頓的滾動(dòng),則會(huì)給用戶(hù)帶來(lái)極大不舒爽的感覺(jué)。
  滾動(dòng)與頁(yè)面渲染的關(guān)系
  為什么滾動(dòng)事件需要去優(yōu)化?因?yàn)樗绊懥诵阅?。那它影響了什么性能呢?這個(gè)就要從頁(yè)面性能問(wèn)題由什么決定說(shuō)起。
  我覺(jué)得搞技術(shù)一定要追本溯源,不要看到別人一篇文章說(shuō)滾動(dòng)事件會(huì)導(dǎo)致卡頓并說(shuō)了一堆解決方案優(yōu)化技巧就如獲至寶奉為圭臬,我們需要的不是拿來(lái)主義而是批判主義,多去源頭看看。
  從問(wèn)題出發(fā),一步一步尋找到最后,就很容易找到問(wèn)題的癥結(jié)所在,只有這樣得出的解決方法才容易記住。
  說(shuō)教了一堆廢話,不喜歡的直接忽略哈,回到正題,要找到優(yōu)化的入口就要知道問(wèn)題出在哪里,對(duì)于頁(yè)面優(yōu)化而言,那么我們就要知道頁(yè)面的渲染原理:
  瀏覽器渲染原理我在我上一篇文章里也要詳細(xì)的講到,不過(guò)更多的是從動(dòng)畫(huà)渲染的角度去講的:【W(wǎng)eb動(dòng)畫(huà)】CSS3 3D 行星運(yùn)轉(zhuǎn)  瀏覽器渲染原理 。
  想了想,還是再簡(jiǎn)單的描述下,我發(fā)現(xiàn)每次 review 這些知識(shí)點(diǎn)都有新的收獲,這次換一張圖,以chrome為例子,一個(gè)Web頁(yè)面的展示,簡(jiǎn)單來(lái)說(shuō)可以認(rèn)為經(jīng)歷了以下下幾個(gè)步驟:
  JavaScript:一般來(lái)說(shuō),我們會(huì)使用JavaScript來(lái)實(shí)現(xiàn)一些視覺(jué)變化的效果。比如做一個(gè)動(dòng)畫(huà)或者往頁(yè)面里添加一些DOM元素等。
  Style:計(jì)算樣式,這個(gè)過(guò)程是根據(jù)CSS選擇器,對(duì)每個(gè)DOM元素匹配對(duì)應(yīng)的CSS樣式。這一步結(jié)束之后,就確定了每個(gè)DOM元素上該應(yīng)用什么CSS樣式規(guī)則。
  Layout:布局,上一步確定了每個(gè)DOM元素的樣式規(guī)則,這一步就是具體計(jì)算每個(gè)DOM元素最終在屏幕上顯示的大小和位置。web 頁(yè)面中元素的布局是相對(duì)的,因此一個(gè)元素的布局發(fā)生變化,會(huì)聯(lián)動(dòng)地引發(fā)其他元素的布局發(fā)生變化。比如,元素的寬度的變化會(huì)影響其子元素的寬度,其子元素寬度的變化也會(huì)繼續(xù)對(duì)其孫子元素產(chǎn)生影響。因此對(duì)于瀏覽器來(lái)說(shuō),布局過(guò)程是經(jīng)常發(fā)生的。
  Paint:繪制,本質(zhì)上就是填充像素的過(guò)程。包括繪制文字、顏色、圖像、邊框和陰影等,也就是一個(gè)DOM元素所有的可視效果。一般來(lái)說(shuō),這個(gè)繪制過(guò)程是在多個(gè)層上完成的。
  Composite:渲染層合并,由上一步可知,對(duì)頁(yè)面中DOM元素的繪制是在多個(gè)層上進(jìn)行的。在每個(gè)層上完成繪制過(guò)程之后,瀏覽器會(huì)將所有層按照合理的順序合并成一個(gè)圖層,然后顯示在屏幕上。對(duì)于有位置重疊的元素的頁(yè)面,這個(gè)過(guò)程尤其重要,因?yàn)橐坏﹫D層的合并順序出錯(cuò),將會(huì)導(dǎo)致元素顯示異常。
  這里又涉及了層(GraphicsLayer)的概念,GraphicsLayer 層是作為紋理(texture)上傳給GPU的,現(xiàn)在經(jīng)常能看到說(shuō)GPU硬件加速,就和所謂的層的概念密切相關(guān)。但是和本文的滾動(dòng)優(yōu)化相關(guān)性不大,有興趣深入了解的可以自行 google 更多。
  簡(jiǎn)單來(lái)說(shuō),網(wǎng)頁(yè)生成的時(shí)候,至少會(huì)渲染(Layout+Paint)一次。用戶(hù)訪問(wèn)的過(guò)程中,還會(huì)不斷重新的重排(reflow)和重繪(repaint)。
  其中,用戶(hù) scroll 和 resize 行為(即是滑動(dòng)頁(yè)面和改變窗口大?。?huì)導(dǎo)致頁(yè)面不斷的重新渲染。
  當(dāng)你滾動(dòng)頁(yè)面時(shí),瀏覽器可能會(huì)需要繪制這些層(有時(shí)也被稱(chēng)為合成層)里的一些像素。通過(guò)元素分組,當(dāng)某個(gè)層的內(nèi)容改變時(shí),我們只需要更新該層的結(jié)構(gòu),并僅僅重繪和柵格化渲染層結(jié)構(gòu)里變化的那一部分,而無(wú)需完全重繪。顯然,如果當(dāng)你滾動(dòng)時(shí),像視差網(wǎng)站(戳我看看)這樣有東西在移動(dòng)時(shí),有可能在多層導(dǎo)致大面積的內(nèi)容調(diào)整,這會(huì)導(dǎo)致大量的繪制工作。
  防抖(Debouncing)和節(jié)流(Throttling)
  scroll事件本身會(huì)觸發(fā)頁(yè)面的重新渲染,同時(shí)scroll事件的handler又會(huì)被高頻度的觸發(fā), 因此事件的handler內(nèi)部不應(yīng)該有復(fù)雜操作,例如DOM操作就不應(yīng)該放在事件處理中。
  針對(duì)此類(lèi)高頻度觸發(fā)事件問(wèn)題(例如頁(yè)面scroll,屏幕resize,監(jiān)聽(tīng)用戶(hù)輸入等),下面介紹兩種常用的解決方法,防抖和節(jié)流。
  防抖(Debouncing)
  防抖技術(shù)即是可以把多個(gè)順序地調(diào)用合并成一次,也就是在一定時(shí)間內(nèi),規(guī)定事件被觸發(fā)的次數(shù)。
  通俗一點(diǎn)來(lái)說(shuō),看看下面這個(gè)簡(jiǎn)化的例子:
// 簡(jiǎn)單的防抖動(dòng)函數(shù)
function debounce(func, wait, immediate) {
// 定時(shí)器變量
 var timeout;
 return function() {
// 每次觸發(fā) scroll handler 時(shí)先清除定時(shí)器
  clearTimeout(timeout);
// 指定 xx ms 后觸發(fā)真正想進(jìn)行的操作 handler<br/  timeout = setTimeout(func, wait);
  };
};
// 實(shí)際想綁定在 scroll 事件上的 handler
function realFunc(){
  console.log("Success");
}
// 采用了防抖動(dòng)
window.addEventListener('scroll',debounce(realFunc,500));
// 沒(méi)采用防抖動(dòng)
window.addEventListener('scroll',realFunc);
  上面簡(jiǎn)單的防抖的例子可以拿到瀏覽器下試一下,大概功能就是如果 500ms 內(nèi)沒(méi)有連續(xù)觸發(fā)兩次 scroll 事件,那么才會(huì)觸發(fā)我們真正想在 scroll 事件中觸發(fā)的函數(shù)。
  上面的示例可以更好的封裝一下:
// 防抖動(dòng)函數(shù)
function debounce(func, wait, immediate) {
  return function() {
  var context = this, args = arguments;
  var later = function() {
  timeout = null;
  if (!immediate) func.apply(context, args);
  };
  var callNow = immediate !timeout;
  clearTimeout(timeout);
  timeout = setTimeout(later, wait);
  if (callNow) func.apply(context, args);
  };
};
 
var myEfficientFn = debounce(function() {
// 滾動(dòng)中的真正的操作
}, 250);
// 綁定監(jiān)聽(tīng)
window.addEventListener('resize', myEfficientFn);
  節(jié)流(Throttling)
  防抖函數(shù)確實(shí)不錯(cuò),但是也存在問(wèn)題,譬如圖片的懶加載,我希望在下滑過(guò)程中圖片不斷的被加載出來(lái),而不是只有當(dāng)我停止下滑時(shí)候,圖片才被加載出來(lái)。又或者下滑時(shí)候的數(shù)據(jù)的 ajax 請(qǐng)求加載也是同理。
  這個(gè)時(shí)候,我們希望即使頁(yè)面在不斷被滾動(dòng),但是滾動(dòng) handler 也可以以一定的頻率被觸發(fā)(譬如 250ms 觸發(fā)一次),這類(lèi)場(chǎng)景,就要用到另一種技巧,稱(chēng)為節(jié)流函數(shù)(throttling)。
  節(jié)流函數(shù),只允許一個(gè)函數(shù)在 X 毫秒內(nèi)執(zhí)行一次,只有當(dāng)上一次函數(shù)執(zhí)行后過(guò)了你規(guī)定的時(shí)間間隔,才能進(jìn)行下一次該函數(shù)的調(diào)用。
  與防抖相比,節(jié)流函數(shù)最主要的不同在于它保證在 X 毫秒內(nèi)至少執(zhí)行一次我們希望觸發(fā)的事件 handler。
  與防抖相比,節(jié)流函數(shù)多了一個(gè) mustRun 屬性,代表 mustRun 毫秒內(nèi),必然會(huì)觸發(fā)一次 handler ,同樣是利用定時(shí)器,看看  簡(jiǎn)單的示例:
// 簡(jiǎn)單的節(jié)流函數(shù)
function throttle(func, wait, mustRun) {
 var timeout,
  startTime = new Date();
  return function() {
  var context = this,
  args = arguments,
  curTime = new Date();
  clearTimeout(timeout);
 // 如果達(dá)到了規(guī)定的觸發(fā)時(shí)間間隔,觸發(fā) handler
  if(curTime - startTime >= mustRun){
 func.apply(context,args);
 startTime = curTime;
// 沒(méi)達(dá)到觸發(fā)間隔,重新設(shè)定定時(shí)器
}else{
timeout = setTimeout(func, wait);
}
};
};
// 實(shí)際想綁定在 scroll 事件上的 handler
function realFunc(){
console.log("Success");
}
// 采用了節(jié)流函數(shù)
window.addEventListener('scroll',throttle(realFunc,500,1000));
  上面簡(jiǎn)單的節(jié)流函數(shù)的例子可以拿到瀏覽器下試一下,大概功能就是如果在一段時(shí)間內(nèi)scroll觸發(fā)的間隔一直短于500ms,那么能保證事件我們希望調(diào)用的handler至少在1000ms內(nèi)會(huì)觸發(fā)一次。

網(wǎng)絡(luò)營(yíng)銷(xiāo)推廣 . 北京匯仁智杰科技有限公司!

地址:北京市昌平區(qū)回龍觀龍冠大廈5層
咨詢(xún):15201492965
業(yè)務(wù)QQ:373002979
E - mail:sales @ huirenzhijie.com
企業(yè)網(wǎng)站備案:京ICP備15021091號(hào)-1

匯仁智杰與眾不同

  • 有網(wǎng)絡(luò)推廣經(jīng)驗(yàn)
  • 有網(wǎng)站建站隊(duì)伍
  • 有大型網(wǎng)站建設(shè)經(jīng)驗(yàn)
  • 致力于營(yíng)銷(xiāo)型網(wǎng)站建設(shè)
  • 始終堅(jiān)持技術(shù)和服務(wù)同樣重要
查看PC版網(wǎng)站
備案號(hào):京ICP備15021091號(hào)-1 版權(quán)所有:匯仁智杰