preface

Hello, everyone, I am Lin Sanxin, with the most popular words to speak the most difficult knowledge point is my motto, the basis is advanced premise is my beginner’s mind, today with everyone to talk, if the back end really returned to the front end of 100,000 pieces of data, we front-end how to gracefully show it? (Assuming the back end can actually send 100,000 pieces of data to the front end)

Pre – work

Do the pre-work well before you test it

The back-end structures,

Create a server.js file, simply create a service, and return 10w pieces of data to the front end. Then use nodemon server.js to start the service

If the nodemon is not installed, you can install NPM I nodemon-g globally first

// server.js

const http = require('http')
const port = 8000;

http.createServer(function (req, res) {
  / / open Cors
  res.writeHead(200, {
    * Allow all domain names
    'Access-Control-Allow-Origin': The '*'.// Allow request methods across domains, or * allow all methods
    "Access-Control-Allow-Methods": "DELETE,PUT,POST,GET,OPTIONS".// The allowed header type
    'Access-Control-Allow-Headers': 'Content-Type'
  })
  let list = []
  let num = 0

  // Generate a list of 100,000 entries
  for (let i = 0; i < 1000000; i++) {
    num++
    list.push({
      src: 'https://p3-passport.byteacctimg.com/img/user-avatar/d71c38d1682c543b33f8d716b3b734ca~300x300.image'.text: I was `${num}Guest Lin Three heart '.tid: num
    })
  }
  res.end(JSON.stringify(list));
}).listen(port, function () {
  console.log('server is listening on port ' + port);
})
Copy the code

The front page

Start with a new index.html

// index.html

/ / style
<style>
    * {
      padding: 0;
      margin: 0;
    }
    #container {
      height: 100vh;
      overflow: auto;
    }
    .sunshine {
      display: flex;
      padding: 10px;
    }
    img {
      width: 150px;
      height: 150px;
    }
  </style>

/ / HTML
<body>
  <div id="container">
  </div>
  <script src="./index.js"></script>
</body>
Copy the code

Then create a new index.js file that wraps an AJAX function to request the 10W data

// index.js

// request function
const getList = () = > {
    return new Promise((resolve, reject) = > {
        // Step 1: Create an asynchronous object
        var ajax = new XMLHttpRequest();
        // Step 2: Set the request URL parameters. Parameter 1 is the request type, parameter 2 is the request URL, can take parameters
        ajax.open('get'.'http://127.0.0.1:8000');
        // Step 3: Send the request
        ajax.send();
        // Step 4: Register the event onreadyStatechange to be called when the status changes
        ajax.onreadystatechange = function () {
            if (ajax.readyState == 4 && ajax.status == 200) {
                // Step 5 If the judgment can be reached, the data returned perfectly, and the requested page exists
                resolve(JSON.parse(ajax.responseText))
            }
        }
    })
}

// Get the Container object
const container = document.getElementById('container')
Copy the code

Direct rendering

The most direct way is to render it directly, but this method is definitely not desirable, because rendering 10W nodes at one time is very time-consuming. Let’s take a look at the time, almost 12 seconds is very time-consuming

const renderList = async() = > {console.time('List time')
    const list = await getList()
    list.forEach(item= > {
        const div = document.createElement('div')
        div.className = 'sunshine'
        div.innerHTML = `<img src="${item.src}" /><span>${item.text}</span>`
        container.appendChild(div)
    })
    console.timeEnd('List time')
}
renderList()
Copy the code

SetTimeout paging rendering

The method is to divide 10W into total math.ceil (total/Limit) pages by the number of pages per page, and then use setTimeout to render one page at a time, which greatly reduces the rendering time of the first page

const renderList = async() = > {console.time('List time')
    const list = await getList()
    console.log(list)
    const total = list.length
    const page = 0
    const limit = 200
    const totalPage = Math.ceil(total / limit)

    const render = (page) = > {
        if (page >= totalPage) return
        setTimeout(() = > {
            for (let i = page * limit; i < page * limit + limit; i++) {
                const item = list[i]
                const div = document.createElement('div')
                div.className = 'sunshine'
                div.innerHTML = `<img src="${item.src}" /><span>${item.text}</span>`
                container.appendChild(div)
            }
            render(page + 1)},0)
    }
    render(page)
    console.timeEnd('List time')}Copy the code

requestAnimationFrame

Use requestAnimationFrame instead of setTimout to reduce the number of rearrangements and greatly improve performance. It is recommended to use requestAnimationFrame for rendering

const renderList = async() = > {console.time('List time')
    const list = await getList()
    console.log(list)
    const total = list.length
    const page = 0
    const limit = 200
    const totalPage = Math.ceil(total / limit)

    const render = (page) = > {
        if (page >= totalPage) return
        // Use requestAnimationFrame instead of setTimeout
        requestAnimationFrame(() = > {
            for (let i = page * limit; i < page * limit + limit; i++) {
                const item = list[i]
                const div = document.createElement('div')
                div.className = 'sunshine'
                div.innerHTML = `<img src="${item.src}" /><span>${item.text}</span>`
                container.appendChild(div)
            }
            render(page + 1)},0)
    }
    render(page)
    console.timeEnd('List time')}Copy the code

Document fragment + requestAnimationFrame

Benefits of document fragmentation

  • 1. Previously, we created one at a timedivTag isappendChildOnce, but there wasDocument fragmentsYou can start with one pagedivTag goes in firstDocument fragmentsMedium, and then onceappendChildtocontainerIn the middle, this is going to decreaseappendChildThe number of times, greatly improved performance
  • 2. The page will only renderDocument fragmentsWrap elements without renderingDocument fragments
const renderList = async() = > {console.time('List time')
    const list = await getList()
    console.log(list)
    const total = list.length
    const page = 0
    const limit = 200
    const totalPage = Math.ceil(total / limit)

    const render = (page) = > {
        if (page >= totalPage) return
        requestAnimationFrame(() = > {
            // Create a document fragment
            const fragment = document.createDocumentFragment()
            for (let i = page * limit; i < page * limit + limit; i++) {
                const item = list[i]
                const div = document.createElement('div')
                div.className = 'sunshine'
                div.innerHTML = `<img src="${item.src}" /><span>${item.text}</span>`
                // Insert the document fragment first
                fragment.appendChild(div)
            }
            // appendChild
            container.appendChild(fragment)
            render(page + 1)},0)
    }
    render(page)
    console.timeEnd('List time')}Copy the code

Lazy loading

To keep things simple, let’s start a VUE front-end project with the back-end services still on

In fact, the implementation principle is very simple, let’s use a diagram to show that, put an empty node blank at the end of the list, render the first page of data, scroll up until Blank appears in the view, indicating the end, then load the second page, and so on.

To tell if blank appears on a view, use the getBoundingClientRect method to get the top property

<script setup lang="ts">
import { onMounted, ref, computed } from 'vue'
const getList = () = > {
  // Same code as above
}

const container = ref<HTMLElement>() / / container node
const blank = ref<HTMLElement>() / / blank nodes
const list = ref<any>([]) / / list
const page = ref(1) // The current number of pages
const limit = 200 // One page display
// Maximum number of pages
const maxPage = computed(() = > Math.ceil(list.value.length / limit))
// A list of real displays
const showList = computed(() = > list.value.slice(0, page.value * limit))
const handleScroll = () = > {
  // Compare the current page count to the maximum page count
  if (page.value > maxPage.value) return
  constclientHeight = container.value? .clientHeightconstblankTop = blank.value? .getBoundingClientRect().topif (clientHeight === blankTop) {
    // Blank appears in the view, the current page count is increased by 1
    page.value++
  }
}

onMounted(async() = > {const res = await getList()
  list.value = res
})
</script>

<template>
  <div class="container" @scroll="handleScroll" ref="container">
    <div class="sunshine" v-for="(item) in showList" :key="item.tid">
      <img :src="item.src" />
      <span>{{ item.text }}</span>
    </div>
    <div ref="blank"></div>
  </div>
</template>
Copy the code

Virtual list

Virtual list needs to explain more, here I share a virtual list of my article, ha ha HA HA HA HA HA HA HA HA HA HA HA HA ha

Combined with “Kangxi Talent Show”, tell us about “virtual list”

conclusion

If you think this article is a little bit helpful to you, click a like and encourage Lin Sanxin haha. Or you can join my shoal of fish to enter the learning group, shoal of fish, please click here to shoal of fish, I will regularly broadcast mock interview, resume guidance, answer questions