asp.net ajax 怎么获取前端ul li_字节前端提前批面试题:触发了几次回流几次重绘...
一道字節(jié)面試題刷新了我的認知,又學到了新知識,開心。
剛開始我說了答案是各3次,因為獲取一次offsetWidth一次,然后改變樣式一次。
但是后來發(fā)現(xiàn)之所以offsetWidth會觸發(fā)重排是因為刷新渲染隊列。而在這道題中,之前的渲染隊列為空,所以不會觸發(fā)重排。所以兩次。
后面發(fā)現(xiàn)又錯了,是因為有渲染隊列,所以因該是一次。不懂渲染隊列沒關(guān)系,往下看,就懂了。
當然要分為古董瀏覽器和現(xiàn)代瀏覽器。現(xiàn)代瀏覽器(也就是說有渲染隊列的)應(yīng)當是一次,古董的應(yīng)當是2次
一.什么是重繪與重排
瀏覽器下載完頁面中的所有組件——HTML標記、JavaScript、CSS、圖片之后會解析生成兩個內(nèi)部數(shù)據(jù)結(jié)構(gòu)——DOM樹和渲染樹。
DOM樹表示頁面結(jié)構(gòu),渲染樹表示DOM節(jié)點如何顯示。DOM樹中的每一個需要顯示的節(jié)點在渲染樹種至少存在一個對應(yīng)的節(jié)點(隱藏的DOM元素disply值為none 在渲染樹中沒有對應(yīng)的節(jié)點)。渲染樹中的節(jié)點被稱為“幀”或“盒”,符合CSS模型的定義,理解頁面元素為一個具有填充,邊距,邊框和位置的盒子。一旦DOM和渲染樹構(gòu)建完成,瀏覽器就開始顯示(繪制)頁面元素。
當DOM的變化影響了元素的幾何屬性(寬或高),瀏覽器需要重新計算元素的幾何屬性,同樣其他元素的幾何屬性和位置也會因此受到影響。瀏覽器會使渲染樹中受到影響的部分失效,并重新構(gòu)造渲染樹。這個過程稱為重排。完成重排后,瀏覽器會重新繪制受影響的部分到屏幕,該過程稱為重繪。由于瀏覽器的流布局,對渲染樹的計算通常只需要遍歷一次就可以完成。但table及其內(nèi)部元素除外,它可能需要多次計算才能確定好其在渲染樹中節(jié)點的屬性,通常要花3倍于同等元素的時間。這也是為什么我們要避免使用table做布局的一個原因。
并不是所有的DOM變化都會影響幾何屬性,比如改變一個元素的背景色并不會影響元素的寬和高,這種情況下只會發(fā)生重繪。
不管頁面發(fā)生了重繪還是重排,它們都會影響性能
二.怎樣觸發(fā)重排
頁面布局和元素幾何屬性的改變就會導(dǎo)致重排 下列情況會發(fā)生重排
- 頁面初始渲染
- 添加/刪除可見DOM元素
- 改變元素位置
- 改變元素尺寸(寬、高、內(nèi)外邊距、邊框等)
- 改變元素內(nèi)容(文本或圖片等)
- 改變窗口尺寸
不同的條件下發(fā)生重排的范圍及程度會不同
某些情況甚至會重排整個頁面,比如滑動滾動條
三.瀏覽器優(yōu)化
例如:
假如我要用js修改某個div的樣式
div.style.left = '10px'; div.style.top = '10px'; div.style.width = '10px'; div.style.height = '10px';我們修改了元素的left、top、width、height屬性 ,滿足我們發(fā)生重排的條件 ,理論上會發(fā)生4次重排 ,但是實際上只會發(fā)生1次重排 ,因為我們現(xiàn)代的瀏覽器都有渲染隊列的機制 ,當我改變了元素的一個樣式會導(dǎo)致瀏覽器發(fā)生重排或重繪時 ,它會進入一個渲染隊列 ,然后瀏覽器繼續(xù)往下看,如果下面還有樣式修改 ,那么同樣入隊 ,直到下面沒有樣式修改 ,瀏覽器會按照渲染隊列批量執(zhí)行來優(yōu)化重排過程,一并修改樣式 ,這樣就把本該4次的重排優(yōu)化為1次
But,當我們寫如下代碼時:
div.style.left = '10px'; console.log(div.offsetLeft);div.style.top = '10px'; console.log(div.offsetTop);div.style.width = '20px'; console.log(div.offsetWidth);div.style.height = '20px'; console.log(div.offsetHeight);還是1次重排嗎?
Obviously not! 此時發(fā)生了4次重排!
上文不是說瀏覽器有渲染隊列優(yōu)化機制嗎? 為什么會有4次?
這和offsetLeft/Top/Width/Height有關(guān)
offsetTop、offsetLeft、offsetWidth、offsetHeight clientTop、clientLeft、clientWidth、clientHeight scrollTop、scrollLeft、scrollWidth、scrollHeight getComputedStyle()(IE中currentStyle)這些會強制刷新隊列要求樣式修改任務(wù)立刻執(zhí)行
因為瀏覽器并不確定在下面的代碼中是否還有修改同樣的樣式,為了獲取到當前正確的的即時值不得不立刻執(zhí)行渲染隊列觸發(fā)重排!!!
四.重繪與重排性能優(yōu)化
1.分離讀寫操作
我們就可以對上面的代碼進行優(yōu)化
div.style.left = '10px'; div.style.top = '10px'; div.style.width = '20px'; div.style.height = '20px';console.log(div.offsetLeft); console.log(div.offsetTop); console.log(div.offsetWidth); console.log(div.offsetHeight);這樣就僅僅發(fā)生1次重排了!
2.樣式集中改變
還是我們最初修改樣式的代碼
div.style.left = '10px'; div.style.top = '10px'; div.style.width = '20px'; div.style.height = '20px';雖然現(xiàn)代瀏覽器有渲染隊列的優(yōu)化機制,但是古董瀏覽器效率仍然底下,觸發(fā)了4次重排 ,即便這樣,我們?nèi)匀豢梢宰龀鰞?yōu)化 ,我們需要cssText屬性合并所有樣式改變
div.style.cssText = 'left:10px;top:10px;width:20px;height:20px;';這樣只需要修改DOM一次一并處理,僅僅觸發(fā)了1次重排 ,而且只用了一行代碼
除了cssText以外,我們還可以通過修改class類名來進行樣式修改
div.className = 'new-class';這種辦法可維護性好,還可以幫助我們免除顯示性代碼,但是會消耗一點點的性能
3.緩存布局信息
div.style.left = div.offsetLeft + 1 + 'px'; div.style.top = div.offsetTop + 1 + 'px';這種讀操作完就執(zhí)行寫操作造成了2次重排
緩存可以進行優(yōu)化
var curLeft = div.offsetLeft; var curTop = div.offsetTop; div.style.left = curLeft + 1 + 'px'; div.style.top = curTop + 1 + 'px';相當于是分離讀寫操作,優(yōu)化為1次重排
4.元素批量操作
現(xiàn)在我們想要向ul中循環(huán)添加大量li (如果ul還不存在,最好的辦法是先循環(huán)添加li到ul,然后再把ul添加到文檔,1次重排)
var ul = document.getElementById('demo'); for(var i = 0; i < 1e5; i++){var li = document.createElement('li');var text = document.createTextNode(i);li.appendChild(text);ul.appendChild(li); }我可以做出下面的優(yōu)化
var ul = document.getElementById('demo'); ul.style.display = 'none'; for(var i = 0; i < 1e5; i++){var li = document.createElement('li');var text = document.createTextNode(i);li.appendChild(text);ul.appendChild(li); } ul.style.display = 'block'; var ul = document.getElementById('demo'); var frg = document.createDocumentFragment(); for(var i = 0; i < 1e5; i++){var li = document.createElement('li');var text = document.createTextNode(i);li.appendChild(text);frg.appendChild(li); }ul.appendChild(frg); var ul = document.getElementById('demo'); var clone = ul.cloneNode(true); for(var i = 0; i < 1e5; i++){var li = document.createElement('li');var text = document.createTextNode(i);li.appendChild(text);clone.appendChild(li); } ul.parentNode.replaceChild(clone,ul);上面的方法減少重繪和重排的原理很簡單
- 元素脫離文檔
- 改變樣式
- 元素回歸文檔
而改變元素就分別使用了隱藏元素、文檔碎片和克隆元素
總結(jié)
以上是生活随笔為你收集整理的asp.net ajax 怎么获取前端ul li_字节前端提前批面试题:触发了几次回流几次重绘...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: excel如何匹配同名数据_Excel如
- 下一篇: ad采集 cube配置 dma_【技术技