Site recomendado LuckyCola para ferramentas front-end essenciais (cama de imagens grátis, API, chatAI, etc.):
https://luckycola.com.cn/
1. Fundo
A maior parte da interação de dados entre front-end e back-end ocorre na forma de interfaces. Às vezes, a quantidade de dados retornados pelo back-end para o front-end é muito grande, e o front-end precisa renderizar esses dados à visualização da página de acordo com as necessidades do negócio, então a quantidade de dados é enorme. Nesse caso, esse consumo de desempenho de front-end também é enorme, então como podemos lidar com essa situação de forma mais eficiente?
2. Introdução ao programa
1. Renderização direta
A maneira mais direta é renderizar todos os dados na página de uma vez. código mostrado abaixo:
const renderList = async () => {
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)
})
}
renderList()
A renderização de 100.000 registros por vez leva cerca de 12 segundos, o que obviamente não é aconselhável.
2. Renderização de paginação por meio de setTimeout
Um método de otimização simples é paginar os dados. Supondo que cada página possua um registro de limite, os dados podem ser divididos em páginas Math.ceil (total/limite). Depois, podemos usar setTimeout para renderizar as páginas sequencialmente, uma página por vez.
const renderList = async () => {
const list = await getList()
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)
}
Após a paginação, os dados podem ser rapidamente renderizados na tela, reduzindo o tempo de página em branco.
3. Renderização de paginação por meio de requestAnimationFrame
Ao renderizar a página, podemos usar requestAnimationFrame em vez de setTimeout, o que pode reduzir o número de refluxos e melhorar o desempenho.
const renderList = async () => {
const list = await getList()
const total = list.length
const page = 0
const limit = 200
const totalPage = Math.ceil(total / limit)
const render = (page) => {
if (page >= totalPage) return
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)
})
}
render(page)
}
O método window.requestAnimationFrame() informa ao navegador que você deseja executar a animação e solicita que o navegador chame a função especificada para atualizar a animação antes do próximo redesenho. Este método toma o retorno de chamada como parâmetro a ser chamado antes de redesenhar.
4. Fragmentos de documentos
Anteriormente, toda vez que um elemento div era criado, o elemento era inserido diretamente na página via appendChild. MasappendChild é uma operação cara.
Na verdade, podemos primeiro criar um fragmento de documento, criar o elemento div e depois inserir o elemento no fragmento de documento. Depois que todos os elementos div forem criados, insira o fragmento na página. Fazer isso também pode melhorar o desempenho da página.
const renderList = async () => {
console.time('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)
}
container.appendChild(fragment)
render(page + 1)
})
}
render(page)
console.timeEnd('time')
}
5. Carregamento lento
Embora o back-end retorne muitos dados de uma vez, a tela do usuário só pode exibir dados limitados ao mesmo tempo. Portanto, podemos usar uma estratégia de carregamento lento para renderizar dados dinamicamente com base na posição de rolagem do usuário.
Para obter a posição de rolagem do usuário, podemos adicionar um nó vazio no final da lista. Sempre que a janela de visualização fica em branco, significa que o usuário rolou até o final da página da web, o que significa que precisamos continuar renderizando os dados.
Ao mesmo tempo, podemos usar getBoundingClientRect para determinar se o espaço em branco está na parte inferior da página.
<script setup lang="ts">
import {
onMounted, ref, computed } from 'vue'
const getList = () => {
// code as before
}
const container = ref<HTMLElement>() // container element
const blank = ref<HTMLElement>() // blank element
const list = ref<any>([])
const page = ref(1)
const limit = 200
const maxPage = computed(() => Math.ceil(list.value.length / limit))
// List of real presentations
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) {
// When the blank node appears in the viewport, the current page number is incremented by 1
page.value++
}
}
onMounted(async () => {
const res = await getList()
list.value = res
})
</script>
<template>
<div id="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>