<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                開篇我們先對上上節介紹的回流與重繪的基礎知識做個復習(跳讀的同學請自覺回到上上節補齊 →\_→)。 **回流**:當我們對 DOM 的修改引發了 DOM 幾何尺寸的變化(比如修改元素的寬、高或隱藏元素等)時,瀏覽器需要重新計算元素的幾何屬性(其他元素的幾何屬性和位置也會因此受到影響),然后再將計算的結果繪制出來。這個過程就是回流(也叫重排)。 **重繪**:當我們對 DOM 的修改導致了樣式的變化、卻并未影響其幾何屬性(比如修改了顏色或背景色)時,瀏覽器不需重新計算元素的幾何屬性、直接為該元素繪制新的樣式(跳過了上圖所示的回流環節)。這個過程叫做重繪。 由此我們可以看出,**重繪不一定導致回流,回流一定會導致重繪**。硬要比較的話,回流比重繪做的事情更多,帶來的開銷也更大。但這兩個說到底都是吃性能的,所以都不是什么善茬。我們在開發中,要從代碼層面出發,盡可能把回流和重繪的次數最小化。 ## 哪些實際操作會導致回流與重繪 要避免回流與重繪的發生,最直接的做法是避免掉可能會引發回流與重繪的 DOM 操作,就好像拆彈專家在解決一顆炸彈時,最重要的是掐滅它的導火索。 觸發重繪的“導火索”比較好識別——只要是不觸發回流,但又觸發了樣式改變的 DOM 操作,都會引起重繪,比如背景色、文字色、可見性(可見性這里特指形如visibility: hidden這樣不改變元素位置和存在性的、單純針對可見性的操作,注意與display:none進行區分)等。為此,我們要著重理解一下那些可能觸發回流的操作。 ### 回流的“導火索” * 最“貴”的操作:改變 DOM 元素的幾何屬性 這個改變幾乎可以說是“牽一發動全身”——當一個DOM元素的幾何屬性發生變化時,所有和它相關的節點(比如父子節點、兄弟節點等)的幾何屬性都需要進行重新計算,它會帶來巨大的計算量。 常見的幾何屬性有 width、height、padding、margin、left、top、border 等等。此處不再給大家一一列舉。有的文章喜歡羅列屬性表格,但我相信我今天列出來大家也不會看、看了也記不住(因為太多了)。我自己也不會去記這些——其實確實沒必要記,?一個屬性是不是幾何屬性、會不會導致空間布局發生變化,大家寫樣式的時候完全可以通過代碼效果看出來。多說無益,還希望大家可以多寫多試,形成自己的“肌肉記憶”。 * “價格適中”的操作:改變 DOM 樹的結構 這里主要指的是節點的增減、移動等操作。瀏覽器引擎布局的過程,順序上可以類比于樹的前序遍歷——它是一個從上到下、從左到右的過程。通常在這個過程中,當前元素不會再影響其前面已經遍歷過的元素。 * 最容易被忽略的操作:獲取一些特定屬性的值 當你要用到像這樣的屬性:offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight 時,你就要注意了! “像這樣”的屬性,到底是像什么樣?——這些值有一個共性,就是需要通過**即時計算**得到。因此瀏覽器為了獲取這些值,也會進行回流。 除此之外,當我們調用了 getComputedStyle 方法,或者 IE 里的 currentStyle 時,也會觸發回流。原理是一樣的,都為求一個“即時性”和“準確性”。 ## 如何規避回流與重繪 了解了回流與重繪的“導火索”,我們就要盡量規避它們。但很多時候,我們不得不使用它們。當避無可避時,我們就要學會更聰明地使用它們。 ### 將“導火索”緩存起來,避免頻繁改動 有時我們想要通過多次計算得到一個元素的布局位置,我們可能會這樣做: ``` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> #el { width: 100px; height: 100px; background-color: yellow; position: absolute; } </style> </head> <body> <div id="el"></div> <script> // 獲取el元素 const el = document.getElementById('el') // 這里循環判定比較簡單,實際中或許會拓展出比較復雜的判定需求 for(let i=0;i<10;i++) { el.style.top = el.offsetTop + 10 + "px"; el.style.left = el.offsetLeft + 10 + "px"; } </script> </body> </html> ``` 這樣做,每次循環都需要獲取多次“敏感屬性”,是比較糟糕的。我們可以將其以 JS 變量的形式緩存起來,待計算完畢再提交給瀏覽器發出重計算請求: ``` // 緩存offsetLeft與offsetTop的值 const el = document.getElementById('el') let offLeft = el.offsetLeft, offTop = el.offsetTop // 在JS層面進行計算 for(let i=0;i<10;i++) { offLeft += 10 offTop += 10 } // 一次性將計算結果應用到DOM上 el.style.left = offLeft + "px" el.style.top = offTop + "px" ``` ### 避免逐條改變樣式,使用類名去合并樣式 比如我們可以把這段單純的代碼: ``` const container = document.getElementById('container') container.style.width = '100px' container.style.height = '200px' container.style.border = '10px solid red' container.style.color = 'red' ``` 優化成一個有 class 加持的樣子: ``` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> .basic_style { width: 100px; height: 200px; border: 10px solid red; color: red; } </style> </head> <body> <div id="container"></div> <script> const container = document.getElementById('container') container.classList.add('basic_style') </script> </body> </html> ``` 前者每次單獨操作,都去觸發一次渲染樹更改,從而導致相應的回流與重繪過程。 合并之后,等于我們將所有的更改一次性發出,用一個 style 請求解決掉了。 ### 將 DOM “離線” 我們上文所說的回流和重繪,都是在“該元素位于頁面上”的前提下會發生的。一旦我們給元素設置 display: none,將其從頁面上“拿掉”,那么我們的后續操作,將無法觸發回流與重繪——這個將元素“拿掉”的操作,就叫做 DOM 離線化。 仍以我們上文的代碼片段為例: ``` const container = document.getElementById('container') container.style.width = '100px' container.style.height = '200px' container.style.border = '10px solid red' container.style.color = 'red' ...(省略了許多類似的后續操作) ``` 離線化后就是這樣: ``` let container = document.getElementById('container') container.style.display = 'none' container.style.width = '100px' container.style.height = '200px' container.style.border = '10px solid red' container.style.color = 'red' ...(省略了許多類似的后續操作) container.style.display = 'block' ``` 有的同學會問,拿掉一個元素再把它放回去,這不也會觸發一次昂貴的回流嗎?這話不假,但我們把它拿下來了,后續不管我操作這個元素多少次,每一步的操作成本都會非常低。當我們只需要進行很少的 DOM 操作時,DOM 離線化的優越性確實不太明顯。一旦操作頻繁起來,這“拿掉”和“放回”的開銷都將會是非常值得的。 ## Flush 隊列:瀏覽器并沒有那么簡單 以我們現在的知識基礎,理解上面的優化操作并不難。那么現在我問大家一個問題: ``` let container = document.getElementById('container') container.style.width = '100px' container.style.height = '200px' container.style.border = '10px solid red' container.style.color = 'red' ``` 這段代碼里,瀏覽器進行了多少次的回流或重繪呢? “width、height、border是幾何屬性,各觸發一次回流;color只造成外觀的變化,會觸發一次重繪。”——如果你立刻這么想了,說明你是個能力不錯的同學,認真閱讀了前面的內容。那么我們現在立刻跑一跑這段代碼,看看瀏覽器怎么說: ![](https://user-gold-cdn.xitu.io/2018/10/4/1663f57519a785ab?w=1284&h=96&f=png&s=18506) 這里為大家截取有“Layout”和“Paint”出鏡的片段(這個圖是通過 Chrome 的 Performance 面板得到的,后面會教大家用這個東西)。我們看到瀏覽器只進行了一次回流和一次重繪——和我們想的不一樣啊,為啥呢? 因為現代瀏覽器是很聰明的。瀏覽器自己也清楚,如果每次 DOM 操作都即時地反饋一次回流或重繪,那么性能上來說是扛不住的。于是它自己緩存了一個 flush 隊列,把我們觸發的回流與重繪任務都塞進去,待到隊列里的任務多起來、或者達到了一定的時間間隔,或者“不得已”的時候,再將這些任務一口氣出隊。因此我們看到,上面就算我們進行了 4 次 DOM 更改,也只觸發了一次 Layout 和一次 Paint。 大家這里尤其小心這個“不得已”的時候。前面我們在介紹回流的“導火索”的時候,提到過有一類屬性很特別,它們有很強的“即時性”。當我們訪問這些屬性時,瀏覽器會為了獲得此時此刻的、最準確的屬性值,而提前將 flush 隊列的任務出隊——這就是所謂的“不得已”時刻。具體是哪些屬性值,我們已經在“最容易被忽略的操作”這個小模塊介紹過了,此處不再贅述。 ## 小結 整個一節讀下來,可能會有同學感到疑惑:既然瀏覽器已經為我們做了批處理優化,為什么我們還要自己操心這么多事情呢?今天避免這個明天避免那個,多麻煩! 問題在于,**并不是所有的瀏覽器都是聰明的**。我們剛剛的性能圖表,是 Chrome 的開發者工具呈現給我們的。Chrome 里行得通的東西,到了別處(比如 IE)就不一定行得通了。而我們并不知道用戶會使用什么樣的瀏覽器。如果不手動做優化,那么一個頁面在不同的環境下就會呈現不同的性能效果,這對我們、對用戶都是不利的。因此,養成良好的編碼習慣、從根源上解決問題,仍然是最周全的方法。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看