How can the front end better display the 100,000 pieces of data returned by the back end?

How can the front end better display the 100,000 pieces of data returned by the back end?

Let’s talk about this today. If the backend really returns 100,000 pieces of data to the frontend, how can we display them elegantly on the frontend?

Preliminary work

Do the preliminary work first, then you can test it later.

Backend construction

Create a new server.js file, start a simple service, return 10w pieces of data to the front end, and start the service through nodemon server.js

If nodemon is not installed, you can install it globally first with npm i nodemon -g

// server.js
const http = require('http')
const port = 8000; 
http.createServer(function (req, res) {
  // Enable Cors
  res.writeHead(200, {
    //Set the domain name allowed across domains. You can also set * to allow all domain names. 'Access-Control-Allow-Origin': '*',
    //Cross-domain allowed request methods, you can also set * to allow all methods "Access-Control-Allow-Methods": "DELETE,PUT,POST,GET,OPTIONS",
    //Allowed header types 'Access-Control-Allow-Headers': 'Content-Type'
  })
  let list = []
  let num = 0
 
  // Generate a list of 100,000 records
  for (let i = 0; i < 1000000; i++) {
    num++
    list.push({
      src: 'https://p3-passport.byteacctimg.com/img/user-avatar/d71c38d1682c543b33f8d716b3b734ca~300x300.image',
      text: `I am guest number ${num} Lin Sanxin`,
      tid: num
    })
  }
  res.end(JSON.stringify(list));
}).listen(port, function () {
  console.log('server is listening on port ' + port);
})

Front-end page

First create 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 part <body>
  <div id="container">
  </div>
  <script src="./index.js"></script>
</body>

Then create a new index.js file and encapsulate an AJAX function to request these 10w data

// index.js 
// Request function const getList = () => {
    return new Promise((resolve, reject) => {
        //Step 1: Create asynchronous object var ajax = new XMLHttpRequest();
        //Step 2: Set the request URL parameters. Parameter 1 is the request type, and parameter 2 is the request URL. You can use ajax.open('get', 'http://127.0.0.1:8000');
        //Step 3: Send request ajax.send();
        //Step 4: Register the onreadystatechange event. When the state changes, ajax.onreadystatechange = function () {
            if (ajax.readyState == 4 && ajax.status == 200) {
                //Step 5 If you can get to this point, it means the data is returned perfectly and the requested page exists resolve(JSON.parse(ajax.responseText))
            }
        }
    })
} 
// Get the container object const container = document.getElementById('container')

Direct Rendering

The most direct way is to render it directly, but this approach is definitely not advisable, because rendering 10w nodes at a time is very time-consuming. Let's take a look at the time consumption. It takes about 12秒, which 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()

setTimeout paging rendering

This method is to divide 10w into a total of Math.ceil(total / limit) pages according to limit the number of pages per page, and then use setTimeout to render one page of data each time. In this way, the time to render the home page data is greatly reduced.

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')
}

requestAnimationFrame

Using requestAnimationFrame instead of setTimeout reduces the number of重排and greatly improves performance. It is recommended that you use requestAnimationFrame more often in 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')
}

Document fragments + requestAnimationFrame

Benefits of Document Fragmentation

1. Previously, appendChild was called every time a div tag was created. However, with document fragments, div tags of one page can be put into the document fragments first, and then appendChild to container at one time. This reduces the number of appendChild calls and greatly improves performance.

2. The page will only render the elements wrapped by the document fragment, but not the document fragment

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>`
                // First insert the document fragment fragment.appendChild(div)
            }
            // One-time appendChild
            container.appendChild(fragment)
            render(page + 1)
        }, 0)
    }
    render(page)
    console.timeEnd('list time')
}

Lazy Loading

For a more popular explanation, let's start a vue front-end project, and the back-end service is still open.

In fact, the implementation principle is very simple. Let's show it through a picture. Put an empty node blank at the end of the list, then render the first page of data first, scroll up, and wait until blank appears in the view, which means the end. Then load the second page, and so on.

As for how to determine whether blank appears on the view, you can use getBoundingClientRect method to get top attribute

<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 node const list = ref<any>([]) // list const page = ref(1) // current page number const limit = 200 // one page display // maximum number of pages const maxPage = computed(() => Math.ceil(list.value.length / limit))
// The actual displayed list const showList = computed(() => list.value.slice(0, page.value * limit))
const handleScroll = () => {
  // Comparison of current page number and maximum page number if (page.value > maxPage.value) return
  const clientHeight = container.value?.clientHeight
  const blankTop = blank.value?.getBoundingClientRect().top
  if (clientHeight === blankTop) {
    // Blank appears in the view, the current page number increases 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>

The above is the details of how the front-end can better display the 100,000 pieces of data returned by the back-end. For more information about the front-end display of the 100,000 pieces of data returned by the back-end, please pay attention to other related articles on 123WORDPRESS.COM!

You may also be interested in:
  • How to display tens of thousands of data at one time with JavaScript
  • js front-end display and processing methods for large amounts of data
  • Java realizes the display of background data on the front end
  • Summary of methods for implementing front-end and back-end data interaction

<<:  Element with selection table to change the check box in the header into text implementation code

>>:  Summary of Textarea line break issues in HTML

Recommend

VMwarea virtual machine installation win7 operating system tutorial diagram

The installation process of VMwarea will not be d...

React implements multi-component value transfer function through conetxt

The effect of this function is similar to vue的pro...

Detailed explanation of the process of building an MQTT server using Docker

1. Pull the image docker pull registry.cn-hangzho...

Let’s talk in detail about how JavaScript affects DOM tree construction

Table of contents Document Object Model (DOM) DOM...

Solution to the problem of mysql master-slave switch canal

After configuring VIP, the error message that app...

JS, CSS style reference writing

CSS: 1. <link type="text/css" href=&q...

Detailed explanation of mysql trigger example

Table of contents What is a trigger Create a trig...

A detailed introduction to JavaScript primitive values ​​and wrapper objects

Table of contents Preface text Primitive types Pr...

Details of 7 kinds of component communication in Vue3

Table of contents 1. Vue3 component communication...

Detailed explanation of persistent storage of redis under docker

In this chapter, we will start to operate redis i...

A brief discussion on Linux signal mechanism

Table of contents 1. Signal List 1.1. Real-time s...

Detailed Explanation of JavaScript Framework Design Patterns

Table of contents mvc mvp mvvm The source of Vue ...

How to solve the problem of case insensitivity in MySQL queries

question Recently, when I was completing a practi...

Vue ElementUI Form form validation

Form validation is one of the most commonly used ...