Performance Optimization: virtual list, how to render dom 10 million data, the page without Caton

Recently made a demand, when a list of about 20,000 data did not allow to make page, if the page 20000 direct rendering data on some low-equipped computer may shine into pages stuck Based on this demand, we to a virtual handwriting list

Thinking

  1. Fixed display list only a small amount of data, such as 60
  2. Keep to insert deleted from the list when scrolling dom
  3. startIndex, endIndex, constantly changing this to get the latest display list
  4. paddingTop, paddingBottom distraction scroll area of ​​the container

First look at when plugged directly into the list of 20000, the performance of the page

you can see the red flame figure has been part of, dom rendering is also time-consuming but also more than 1s

Then look at the performance of the page when using virtual list

can be seen from the figure flame, a green flame figure, which proves to be rendered by a list of the virtual page performance has been greatly improved

Simple demo implement virtual list

We assume there is a container, 600px height, 30px height of each list item, then we can calculate the total height of the roll container according to the length of the list may also know the height of the display 60 data, at this time we can give the container plus a paddingBottom, to distraction container to simulate the height of the page should scroll

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

Container also need paddingTop used when the container is removed after rolling the top of the data hold up scrollTop

Finally, we need to scroll event listener container to constant changes paddingTop, paddingBottom, startIndex, endIndex

final effect

Finally, attach all of the 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: 300px;
            height: 600px;
            overflow: auto;
            border: 1px solid;
            margin: 100px auto;
        }
        .item {
            height: 29px;
            line-height: 30px;
            border-bottom: 1px solid #aaa;
            padding-left: 20px;
        }
    </style>
</head>
<body>
<div id="app">
    <button @click="add">增加</button>
    <div class="container" ref="container">
        <div class="scroll-wrapper" :style="style">
            <div v-for="(item, index) in scrollList" :key="index" class="item">{{item}}</div>
        </div>
    </div>
</div>
<script src="./vue.js"></script>
<script>
    new Vue({
        el: '#app',
        data: {
            list: [
                '测试数据'
            ],
            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(50000).fill(0)
                arr = arr.map((item, index) => {
                    return index
                })
                this.list = [
                    ...this.list,
                    ...arr
                ]
            }
        }
    })
</script>
</body>
</html>

Guess you like

Origin www.cnblogs.com/songbw/p/11613869.html