Performance Optimization: virtual list, how to render the dom of 50000 pieces of data without jamming at the same time

A recent requirement is that when there are about 50000 pieces of data in the list, it is not allowed to be paged. If the page directly renders 50000 pieces of data, the page may get stuck on some low configuration computers. Based on this requirement, research and analyze the reasons for the front-end rendering jamming, and prepare to hand write a virtual list.

1. Implementation ideas

  1. Only a small amount of data is displayed in the list, such as 60 items
  2. When the list scrolls, you constantly insert and delete DOMS
  3. startIndex, endIndex, and constantly change this value to obtain the latest display list
  4. paddingTop and paddingBottom open the rolling area of the container

First, let's look at the performance of the page when 100000 lists are inserted directly.

 

2. Mode comparison

Situation 1

  • Directly render 50000 pieces of data

 

You can see that there is already a red part in the flame diagram, and dom rendering takes more than 5s

  • Effect before optimization

 

 

Situation II

  • Virtual list loading mode is adopted

Suppose there is a container with a height of 600px and the height of each list item is 30px, then we can calculate the total height of the scrolling container according to the length of the list, and we can also know the height of displaying 60 pieces of data. At this time, we can add a paddingBottom to the container to open the container to simulate the height that the page should scroll

this.paddingBottom = this.allHeight - this.scrollList.length * 30

The container also needs paddingTop to support the scrollTop when the data at the top of the container is removed

Finally, we need to listen to the rolling events of the container to constantly modify paddingTop, paddingBottom, startIndex and endIndex.

 

After adjusting to the virtual list, you can see that the loading rendering is about 1 second!

 

  • Optimized effect

3. Implementation code

<!doctype html>
<html lang="zh">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport"
            content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
        <style>
            .container {
                width: 500px;
                height: 600px;
                overflow: auto;
                border: 1px solid;
                margin: 100px auto;
            }

            .item {
                height: 29px;
                line-height: 30px;
                border-bottom: 1px solid #aaa;
                padding-left: 20px;
                display: flex;
                text-align: center;
            }
            .item span{
                flex: 1;
            }
        </style>
    </head>
    <body>
        <div id="app">
            <button @click="add">Add data</button>
            <div class="container" ref="container">
                <div class="scroll-wrapper" :style="style">
                    <div v-for="(item, index) in scrollList" :key="index" class="item">
                        <span>{{item.name}}</span>
                        <span>{{item.price}}</span>
                        <span>{{item.category}}</span>
                    </div>
                </div>
            </div>
        </div>
        <script src="./js/vue.js"></script>
        <script>
            new Vue({
                el: '#app',
                data: {
                    list: [
                        {name:'name',price:'Unit Price',category:'type'}
                    ],
                    startIndex: 0,
                    endIndex: 60,
                    paddingTop: 0,
                    paddingBottom: 0,
                    allHeight: 0
                },
                computed: {
                    scrollList() {
                        return this.list.slice(this.startIndex, this.endIndex)
                    },
                    style() {
                        return {
                            paddingTop: this.paddingTop + 'px',
                            paddingBottom: this.paddingBottom + 'px'
                        }
                    }
                },
                watch: {
                    list(val) {
                        const valLen = val.length
                        this.allHeight = valLen * 30
                        this.paddingBottom = this.allHeight - this.scrollList.length * 30
                    }
                },
                mounted() {
                    const container = this.$refs.container
                    container.addEventListener('scroll', () => {
                        const top = container.scrollTop
                        this.startIndex = Math.floor(top / 30)
                        this.endIndex = this.startIndex + 60

                        this.paddingTop = top
                        if (this.endIndex >= this.list.length - 1) {
                            this.paddingBottom = 0
                            return
                        }
                        this.paddingBottom = this.allHeight - 600 - top
                    })
                },
                methods: {
                    add() {
                        let arr = new Array(100000).fill(0)
                        arr = arr.map((item, index) => {
                            return { name: "name_" + index, price: Math.ceil(Math.random()*10)+'element', category: Math.random() > 0.5 ? 'Vegetables' : 'Fruits' }
                        })
                        this.list = [
                            ...this.list,
                            ...arr
                        ]
                    }
                }
            })
        </script>
    </body>
</html>

Summary: the continuous operation of dom nodes will lead to slow rendering and very stuck scrolling. The rendering efficiency and scrolling fluency are greatly improved by means of virtual list!

Keywords: Front-end Vue list

Added by darkerstar on Tue, 28 Dec 2021 15:34:59 +0200