簡(jiǎn)介
1 .使用到滾動(dòng)的場(chǎng)景
1 .懶加載
2 .loadmore
3 .affix.固定位置
4 .回到頂部
2 .HTML5標(biāo)準(zhǔn).scroll事件是每幀觸發(fā)一次
幀維度解析渲染過(guò)程
1 .瀏覽器渲染頁(yè)面的renderer進(jìn)程里面,涉及到兩個(gè)進(jìn)程.兩個(gè)進(jìn)程之間通過(guò)commit的消息保持同步
2 .Main進(jìn)程:瀏覽器渲染的主要執(zhí)行步驟.包含從js執(zhí)行到Composite合成的一系列操作
3 .Compositor線程:接收用戶的一些交互操作,比如滾動(dòng)->喚醒進(jìn)程進(jìn)行操作->接收Main線程的操作結(jié)果->commit給真正把頁(yè)面draw到屏幕上的GPU進(jìn)程
主線程里的操作太多,耗時(shí)長(zhǎng),commit的時(shí)間被推遲.瀏覽器來(lái)不及間頁(yè)面draw到屏幕上,就丟失了一幀
4 .Main線程完成最后之后,與Compositor線程使用commit進(jìn)行通信,Compositor調(diào)起Compositor來(lái)處理頁(yè)面
5 .頁(yè)面Paint結(jié)束之后,這一幀就結(jié)束了.GPU進(jìn)程里的GPU進(jìn)程負(fù)責(zé)把Render進(jìn)程操作好的頁(yè)面,交由GPU內(nèi)方法,由GPU把頁(yè)面draw到屏幕上
6 .屏幕刷新,我們就在瀏覽器上看到了新頁(yè)面
瀏覽器主要執(zhí)行步驟
1 .JavaScript:包含與視覺(jué)變化效果相關(guān)的js操作.包括不限于:Dom更新,元素樣式動(dòng)態(tài)改變
2.Style:樣式計(jì)算.瀏覽器根據(jù)css選擇器計(jì)算哪些元素應(yīng)該用哪些規(guī)則,然后間樣式規(guī)則落實(shí)到每個(gè)元素上面,確定每個(gè)元素具體的樣式
3 .Layout:布局.在知道每一個(gè)元素應(yīng)用哪些規(guī)則后,瀏覽器即可開(kāi)始計(jì)算他占據(jù)的空間大小以及在屏幕中的位置
1 .避免layout的操作,將DOM脫離文檔流在對(duì)其進(jìn)行操作,所有操作完畢之后再添加到文檔流中,這樣可以將重排以及重繪的次數(shù)降低到一次或兩次
2 .display:none
3 .DocumentFragment
4 .將原始元素拷貝到一個(gè)脫離文檔的節(jié)點(diǎn)中,修改這個(gè)副本,完成后替換掉原始元素
4 .Painting:繪制.繪制時(shí)填充像素的過(guò)程.涉及繪出文本,顏色,圖像,邊框和陰影.基本上包括元素的每個(gè)可視部分,繪制一般時(shí)在多個(gè)層上完成的.
1 .painting和draw的區(qū)別:paint是把內(nèi)容填充到頁(yè)面,而draw是把頁(yè)面反映到屏幕上
5 .Composite:合成.由于頁(yè)面的各部分可能被繪制到多層.因此他們還需要按照正確的順序繪制到屏幕上,以便正確渲染頁(yè)面.對(duì)于與另一個(gè)元素重疊的元素來(lái)說(shuō).這很重要,因?yàn)橐粋€(gè)錯(cuò)誤可能使另一個(gè)元素錯(cuò)誤的出現(xiàn)在另一個(gè)元素上面
6 .并不是每次都會(huì)走到全部的流程.
7 .重排:會(huì)走完整的流程,改變了元素的幾何屬性(寬度,高度,左側(cè)或者頂部位置等.必須檢查所有的其他元素).任何受印象的部分都需要重新繪制.而且最終繪制的元素需要重新合成,重排進(jìn)行了管道的每一步,性能受到很大的影響
8 .重繪:和上面相比不需要layout:如果是背景圖片,文字顏色或者陰影的話,不會(huì)影響頁(yè)面布局的屬性,瀏覽器會(huì)跳過(guò)布局
9 .不重排和重繪的情況:僅僅使用transform和opacity來(lái)實(shí)現(xiàn)動(dòng)畫(huà),會(huì)避免Layout和Paint操作
10 .每個(gè)層中對(duì)這個(gè)層進(jìn)行 Layout 或者 Paint 是不會(huì)影響其他層的智袭,一般會(huì)根據(jù)整個(gè)頁(yè)面的語(yǔ)義將頁(yè)面分為幾個(gè)層。
11 .查詢css屬性變化導(dǎo)致的操作https://csstriggers.com/
優(yōu)化操作
1 .避免在scroll事件中修改樣式屬性/將樣式操作從scroll事件中剝離
1 .變量的初始化,不依賴滾動(dòng)位置變化的計(jì)算都應(yīng)該在scroll事件外提前就緒
2 .使用web worker分離和頁(yè)面渲染無(wú)關(guān)的邏輯計(jì)算
3 .觸發(fā)監(jiān)聽(tīng)事件時(shí)使用函數(shù)節(jié)流或者去抖
4 .使用requestAnimationFrame提代定時(shí)器:瀏覽器每一次重回前觸發(fā)這個(gè)鉤子
1 .不要在 rAF 的回調(diào)函數(shù)中先修改樣式,再查詢樣式域帐,這樣就失去了 rAF 的作用睬隶〔宴郑可以將對(duì)樣式的查詢提前到回調(diào)函數(shù)中或者 rAF 中盡量靠前的位置
2 .動(dòng)畫(huà)是由瀏覽器按照一定的頻率一幀一幀的繪制的议纯,由css實(shí)現(xiàn)的動(dòng)畫(huà)的優(yōu)勢(shì)就是瀏覽器知道動(dòng)畫(huà)的開(kāi)始及每一幀的循環(huán)間隔崩溪,能夠在恰當(dāng)?shù)臅r(shí)間刷新UI浅役,給用戶一種流暢的體驗(yàn),而setInterval或setTimeout實(shí)現(xiàn)的JavaScript動(dòng)畫(huà)就沒(méi)有這么可靠了伶唯,因?yàn)闉g覽器壓根就無(wú)法保證每一幀渲染的時(shí)間間隔觉既,一般情況下,每秒平均刷新次數(shù)能夠達(dá)到60幀抵怎,就能夠給人流暢的體驗(yàn)奋救,即每過(guò) 1000/60 毫秒渲染新一幀即可,但從上面的例子知反惕,這一點(diǎn)單靠定時(shí)器是無(wú)法保證的。
為此演侯,requestAnimationFrame應(yīng)運(yùn)而生姿染,其作用就是讓瀏覽器流暢的執(zhí)行動(dòng)畫(huà)效果。可以將其理解為專(zhuān)門(mén)用來(lái)實(shí)現(xiàn)動(dòng)畫(huà)效果的api悬赏,通過(guò)這個(gè)api,可以告訴瀏覽器某個(gè)JavaScript代碼要執(zhí)行動(dòng)畫(huà)狡汉,瀏覽器收到通知后,則會(huì)運(yùn)行這些代碼的時(shí)候進(jìn)行優(yōu)化闽颇,實(shí)現(xiàn)流暢的效果盾戴,而不再需要開(kāi)發(fā)人員煩心刷新頻率的問(wèn)題了
3 .requestAnimationFrame接受一個(gè)動(dòng)畫(huà)執(zhí)行函數(shù)作為參數(shù),這個(gè)函數(shù)的作用是僅執(zhí)行一幀動(dòng)畫(huà)的渲染兵多,并根據(jù)條件判斷是否結(jié)束尖啡,如果動(dòng)畫(huà)沒(méi)有結(jié)束,則繼續(xù)調(diào)用requestAnimationFrame并將自身作為參數(shù)傳入剩膘。從示例來(lái)看衅斩,得到了效果平滑流暢的動(dòng)畫(huà),這樣就巧妙地避開(kāi)了每一幀動(dòng)畫(huà)渲染的時(shí)間間隔問(wèn)題
function animationWidth() {
var div = document.getElementById('box');
div.style.width = parseInt(div.style.width) + 1 + 'px';
if(parseInt(div.style.width) < 200) {
requestAnimationFrame(animationWidth)
}
}
requestAnimationFrame(animationWidth);
5 .requestIdleCallback():會(huì)在瀏覽器的每一幀末尾觸發(fā).而且是空閑的時(shí)候,如果不空閑,就推遲到下一幀.適合于執(zhí)行在后臺(tái)運(yùn)行或者空閑時(shí)觸發(fā)回調(diào),否則推遲到下一幀.
避免強(qiáng)制重排:當(dāng)我們很頻繁的執(zhí)行下面這樣的特殊操作.就會(huì)打斷瀏覽器的節(jié)奏
1 .當(dāng)訪問(wèn)scrollWidth,clientWidth,offsetTop,computedStyle登屬性時(shí),會(huì)觸發(fā)這個(gè)效果,導(dǎo)致style和layout前移到j(luò)s代碼執(zhí)行過(guò)程中
2 .
elem.offsetLeft, elem.offsetTop, elem.offsetWidth, elem.offsetHeight, elem.offsetParent
elem.clientLeft, elem.clientTop, elem.clientWidth, elem.clientHeight
elem.getClientRects(), elem.getBoundingClientRect()
elem.scrollWidth, elem.scrollHeight
elem.scrollLeft, elem.scrollTop
//這里不是求當(dāng)前div的滾動(dòng)距離的操作么,這個(gè)也會(huì)造成重排么?
以上的特殊讀操作,一定要使用requestAnimationFrame()包裹起來(lái).避免單個(gè)裸奔
提升合成層
1 .一些屬性會(huì)讓元素們創(chuàng)建出不同的渲染層
1 .relative,fixed,sticky,absolute
2 .有透明度的屬性
3 .css濾鏡
4 .css transform屬性
2 .達(dá)成一些條件,渲染條件會(huì)提升為合成層
1 .一鍵加速的iframe,iframe嵌入的頁(yè)面中有合成層
2 .3D或者硬件加速的2dCanvas
3 .video元素
4 .3d transform
5 .will-change:設(shè)置為opacity,transoorm,top,left,bottom,
6 .對(duì)opacity,tramsform,filter,backgroundFilter應(yīng)用了animation或者transition
3 .合成層之后,會(huì)交由GPU處理,比CPU處理快,但需要重繪的時(shí)候,只需要重繪自己的層,不會(huì)影響到其他的層