current position:Home>Implementation idea of virtual list (equal height / non equal height) - with source code

Implementation idea of virtual list (equal height / non equal height) - with source code

2021-08-27 05:44:55 I once looked across the shore

Virtual list

Let everyone know ,DOM Quantity is one of the most direct reasons affecting site performance , How to effectively control DOM Number , Improving page performance is to improve user stickiness , One of the key means of product conversion , This article will explain how to Destroy the withered and decadent Complete the implementation of a virtual list

Text

Let's first look at the difference in rendering time between normal long lists and virtual lists

Normal page

Let's simulate a page first :

  <div id="content">
    <div class="item">
       <div class="item-animation"></div> * 10
    </div> * 2000
  </div>
 Copy code 
  .content {
    height: 100%;
  }
  .item-animation {
    transform: translate(0);
    transtion: transfrom .5s;
  }
  .item-animation--active {
    transform: translate(10px);
  }
 Copy code 
  const item = document.getElementsByClassName('item')[0]
  item.onclick = function() {
    Array.from(item.children).forEach(div => {
      div.classList.add('item-animation--active')
    })
  }
 Copy code 

Above we define a long list , One has 2000 There are... Inside 10 Of child elements item Elements , Click on item When you do, you let the child elements item-animation Trigger an animation that moves to the right , Let's measure the current trigger How long does it take to execute :

normal.jpg We can find that in the current execution javascipt Click on Task The execution time of is 51ms, This seriously exceeds our normal refresh fluency standard 16.7ms once , This means that the behavior makes the page drop 2 Around the frame , This will greatly reduce user retention , From the figure below, we can see that... Is generated at the end renderLayerTree The time is 7.13ms, It is precisely because the element volume in the page is too large , It takes too long to update the render layer in the last step of rendering .

u.jpg

Virtual list

Same example above , Let's take a look again. Click the first item How long did the triggered sub element animation render take , Through the following figure, we can find that the same animation has the same number of sub elements ,Task The execution time is 1.21ms, This is shorter than in the above example 50 Times of time . vscroll.jpg

Realization

precondition

When implementing this requirement, we need to understand the following knowledge points :

  1. javascript Acquired DOMOM It's not in renderTree Upper renderObject, Just as we use javascript Can be obtained display:none The performance of the nodes is consistent , We are from DOMTree Go up and get it DOM
  2. DOMTree Updates are real-time
  3. Task It is divided into MacroTask And MicroTask, Every MacroTask After execution, it will be handed over to the rendering process to perform a rendering operation

More details can be read You understand wrong nextTick

Contour virtual list

Same example above , Let's set up HTML

  <div class="content">
    <div class="virtual-content"></div>
    <div class="real-content"></div>
  </div>
 Copy code 
  html, body, #app {
    height: 100%;
    width: 100%;
  }
  .content {
    position: relative;
    height: 100%;
    overflow-y: auto;
  }
  .real-content {
    position: absolute;
    width: 100%;
    height: 100%;
    left: 0;
    top: 0;
  }
 Copy code 

content: A container for real content , Height is inherited from the parent 100%
virtual-content: open content Height
real-content: Load the information that needs to be displayed in the visual window

We default to all item They are all equal height Of , So let's take a look at the train of thought :

  1. First of all, we need to know the height when all nodes are loaded in the document , We need to be in Promise In micro tasks Load all nodes into the container virtual-content in , At this time the container virtual-content The corresponding DOM stay DOMTree It will update its attribute value ( For example, height ), At this point we can go through js To get virtual-content Height ,virual-content It is used to support the whole height , So as to simulate rolling The role of , After obtaining the height, assign it to virtual-content( To ensure that all child elements are deleted later virtual-content Still able to stretch the height )

  2. Then we remember that the visual height of the current screen is the same as that of the current single screen item Height ( Include margin, border, padding etc. ), In this way, we can figure out how many visual images can be loaded under the current screen size item The number of :

    • size = clientHeight / itemHeight

And then All data uses set variables childrenSet cache ( Stored for later data display ), Then empty virtual-content The content of . In this case The one above us 1 2 This step uses the third content of preconditions to complete the pre operation

  1. Definition start(childrenSet The beginning of the intercept start), end(childrenSet At the end of the intercept end), end The definition of is very simple :

    • end = start( Where the data begins to be intercepted ) + size( Visual area item Number )
  2. monitor content Rolling , Constantly refresh start Value ,start The value of is actually content Of scrollTop Divide itemHeight Remove the value ( Because we have to simulate item The process of drawing itself out of the visible area )

    • start = Math.floor(scrollTop / itemHeight)
  3. real-content Because it is an absolute positioning layout , So it will follow content Scroll to draw the visible area , So we need to use transform: translateY Draw it back into the visual area , But in order to simulate the real sliding scene ( because item It has its own height , stay item When part is marked out ,real-content It doesn't need to be pulled back into the visual area ), We need to calculate to set the corresponding translateY value , So we said in the last step start The value of plays a key role , because start The value of is scrollTop Divide itemHeight The lower limit of , The extra residual value is actually itemHeight, The values in this part are used to simulate item The process of marking out , We don't need to do any calculations , Finally, we just need to real-content Pull back to start * itemHeight that will do , This completes an idea of contour virtual rolling

 Unnamed file .png

Non contour virtual list

The difference between unequal height and equal height is actually due to item The height of cannot be determined , This leads to how many... Can be contained in the visual area item Of size Not sure , In the end, it is impossible to determine end The cut-off position , The idea of this point is actually very simple , Listen to me ~

The front details are basically consistent with the contour , Let's focus on how to determine size This process

Specific ideas

  1. Before we save the corresponding item Node to childrenSet At the same time in the collection , We need to set up another set childHeightSet: For preservation item Corresponding itemHeight.
  2. childHeightSet And childrenSet It's one-to-one , That is, the same subscript value ,childrenHeightSet The value is childrenSet The height of the value , With this feature, we can do this
  3. First of all get start You can't just use scrollTop / itemHeight, But need to compare scrollTop And childrenHeightSet Before n A cumulative value . When scrollTop Greater than the cumulative value , shows childrenSet We haven't reached the intercept location yet ; if scorllTop <= Cumulative value , The current item Has been slid to the top of the visible area , that start The value of is the current subscript value
 function getStart(scrollTop) {
   var height = 0
   var start = 0
   var i = 0
   while(true) {
     const currentItem = childrenHeight[i]
     if (currentItem) {
       height += currentItem
       if (height >= scrollTop) {
         start = i
         break
       }
     } else {
       break
     }
     i++
   }

   return start
 }
 Copy code 

4. determine size( Finally get directly end), We need to use the height of the current visual area (screenClientHeight) Then compare childrenHeightSet in start After subscript value, the accumulated value . When screenClientHeight Greater than the cumulative value , shows childrenSet Not yet end The location of ; if screenClientHeight <= Cumulative value , The current item It's already the bottom of the visual area , that end The value of is the current subscript value , In this way, the problem of non equal height virtual list is solved !

Conclusion

Thank you for watching !

Source code address

Virtual list

copyright notice
author[I once looked across the shore],Please bring the original link to reprint, thank you.
https://en.qdmana.com/2021/08/20210827054451182c.html

Random recommended