13
头图

Preface

Hello everyone, I’m Lin Sanxin, most difficult knowledge points in the most popular is my motto, is the prerequisite for advanced is my original intention, today I will chat with you, if the back end is true 100,000 pieces of data returned to the front-end, how do we elegantly display it on the front-end? (Haha assuming that the back end can really transmit 100,000 data to the front end)

image.png

Pre-work

Do the preparatory work first before testing

Back-end construction

Create a server.js file 061a4494445370, simply start a service, and return to the front end 10w data, and start the service nodemon server.js

Students who have not installed nodemon npm i nodemon -g
// server.js

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

http.createServer(function (req, res) {
  // 开启Cors
  res.writeHead(200, {
    //设置允许跨域的域名,也可设置*允许所有域名
    'Access-Control-Allow-Origin': '*',
    //跨域允许的请求方法,也可设置*允许所有方法
    "Access-Control-Allow-Methods": "DELETE,PUT,POST,GET,OPTIONS",
    //允许的header类型
    'Access-Control-Allow-Headers': 'Content-Type'
  })
  let list = []
  let num = 0

  // 生成10万条数据的list
  for (let i = 0; i < 1000000; i++) {
    num++
    list.push({
      src: 'https://p3-passport.byteacctimg.com/img/user-avatar/d71c38d1682c543b33f8d716b3b734ca~300x300.image',
      text: `我是${num}号嘉宾林三心`,
      tid: num
    })
  }
  res.end(JSON.stringify(list));
}).listen(port, function () {
  console.log('server is listening on port ' + port);
})

Front page

Create a new index.html

// index.html

// 样式
<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>

Then create a index.js file 061a4494445424, encapsulate a AJAX , used to request the 10w data

// index.js

// 请求函数
const getList = () => {
    return new Promise((resolve, reject) => {
        //步骤一:创建异步对象
        var ajax = new XMLHttpRequest();
        //步骤二:设置请求的url参数,参数一是请求的类型,参数二是请求的url,可以带参数
        ajax.open('get', 'http://127.0.0.1:8000');
        //步骤三:发送请求
        ajax.send();
        //步骤四:注册事件 onreadystatechange 状态改变就会调用
        ajax.onreadystatechange = function () {
            if (ajax.readyState == 4 && ajax.status == 200) {
                //步骤五 如果能够进到这个判断 说明 数据 完美的回来了,并且请求的页面是存在的
                resolve(JSON.parse(ajax.responseText))
            }
        }
    })
}

// 获取container对象
const container = document.getElementById('container')

Direct rendering

The most direct way is to render it directly, but this approach is definitely not advisable, because it 10w nodes at one time. Let’s take a look at the time-consuming, which takes about 12 seconds, which is very Time consuming

截屏2021-11-18 下午10.07.45.png

const renderList = async () => {
    console.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('列表时间')
}
renderList()

setTimeout pagination rendering

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

截屏2021-11-18 下午10.14.46.png

const renderList = async () => {
    console.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('列表时间')
}

requestAnimationFrame

Use requestAnimationFrame instead of setTimeout , which reduces the rearrangements of 161a449444557e and greatly improves performance. It is recommended that you use requestAnimationFrame

const renderList = async () => {
    console.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代替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('列表时间')
}

Document fragment + requestAnimationFrame

document fragmentation

  • 1. Previously, each time a div label was appendChild , 061a449444566f once, but with document fragments, you can first put 1 page of div labels into document fragments, and then one-time appendChild to container , thus reducing appendChild Times, greatly improving performance
  • 2. The page will only render document fragments, and will not render the document fragments
const renderList = async () => {
    console.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(() => {
            // 创建一个文档碎片
            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>`
                // 先塞进文档碎片
                fragment.appendChild(div)
            }
            // 一次性appendChild
            container.appendChild(fragment)
            render(page + 1)
        }, 0)
    }
    render(page)
    console.timeEnd('列表时间')
}

Lazy loading

For a more popular explanation, let’s start a vue front-end project, 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 data on page 1 first, scroll up, and wait until blank appears in the view, it’s the end of the explanation, and then Load the second page, and so on.

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

截屏2021-11-18 下午10.41.01.png

<script setup lang="ts">
import { onMounted, ref, computed } from 'vue'
const getList = () => {
  // 跟上面一样的代码
}

const container = ref<HTMLElement>() // container节点
const blank = ref<HTMLElement>() // blank节点
const list = ref<any>([]) // 列表
const page = ref(1) // 当前页数
const limit = 200 // 一页展示
// 最大页数
const maxPage = computed(() => Math.ceil(list.value.length / limit))
// 真实展示的列表
const showList = computed(() => list.value.slice(0, page.value * limit))
const handleScroll = () => {
  // 当前页数与最大页数的比较
  if (page.value > maxPage.value) return
  const clientHeight = container.value?.clientHeight
  const blankTop = blank.value?.getBoundingClientRect().top
  if (clientHeight === blankTop) {
    // blank出现在视图,则当前页数加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>

Virtual list

There are more virtual lists that need to be explained. Here I share an article of my virtual list, haha I think it’s good, hahahahahaha

combined with "Kangxi Draft", I will tell you about the "virtual list"

Concluding remarks

If you think this article is of little help to you, please give me a thumbs up and encourage Lin Sanxin haha. Or you can join my fish-fishing group. Let's study hard together, ah ah ah ah ah, I will mock interviews regularly, resume guidance, answer questions and answer questions, let's learn from each other and make progress together! !
截屏2021-11-28 上午9.43.19.png


Sunshine_Lin
2.1k 声望7.1k 粉丝