preface
In mobile projects, we usually use lazy loading to load lists, which has the obvious advantage of not having to request the data all at once, but using Ajax to dynamically pull the following data from the server when the user drops down to the bottom. The problem with this is that if the user goes crazy with the pull-down, it will cause the browser to create redundant nodes, which will be redundant, and the VUE will diff as many nodes as you have, which will lead to redundant performance and memory usage. Imagine solving this problem if we could render only nodes in the user’s visual area, which is the background for virtual scrolling.
The principle of virtual scrolling
First of all, we want to know, the virtual rolling is to use Vue v – for implementation, it also explains, virtual scroll is rendering viewing area, only then we viewing area node content is bound to be changed by the user of the change of the scroll bar, suppose that a page can show n nodes, so how do we make this n need to change the node with the scroll bar to move?
Using CSStransform:translateY(), we just need to make the n nodes follow the scroll bar, where we scroll, where the node is replaced.
To implement virtual scrolling you only need to know the following:
- How many items can be displayed on a page?
- Which node should I start rendering from?
- When do you render?
- How to apply colours to a drawing
How many items can a page display?
Page size = page size (clientHeight)/ single item size
volume=Math.ceil(clientHeight/itemHeight)+4;
Why do we have a plus 4 here, and we’ll talk about that later
Which node should I start rendering from?
Let’s assume that the scrollbar is now at x, can we calculate how many nodes this x height can hold, and then figure out which node to start rendering from? The answer is yes, js provides us with the property scrollTop to get the height of the scroll bar.
getCurStart(scrollTop){
// How many books are there
return Math.floor(scrollTop/(itemHeight));
}
Copy the code
When do you render?
Rendering time is also very simple. We need to render when the first node in the list is completely involved, because at this point the node is completely invisible, we need to top it down and then render it into the next item
As shown in the image above, when the 1 is rolled out (completely out of our viewable area), we use the translateY of CSS to top it off and render it to 2. You will notice that there is an extra node outside the viewable area. To ensure the continuity of the slide, you can set up several redundant nodes.
How to apply colours to a drawing
This is the core code. There is a problem here, because JS does not respond to the high frequency callback every time, if you do not get an offset divisible by itemHeight, you will most likely see that the offset of the first node is not 0 when you pull back.
onScroll(){
// The scrollTop constant records the height of the current scroll
const scrollTop=this.$refs.list.scrollTop;
const start=this.getCurStart(scrollTop);
// Compare the last start node to see if it has changed, and then re-render the list
if(this.start! =start){// We need to get a number divisible by itemHeight as the offset of the item
const offsetY = scrollTop - (scrollTop % this.itemHeight);
// Use slice to get the part that needs to be rendered
this.renderList=this.list.slice(start,this.start+this.volume);
TranslateY transform:translateY(${top}px)
this.top=offsetY;
}
this.start=start;
},
Copy the code
Optimization of processing
OnScroll is a high-frequency trigger callback. In order to save performance consumption, we need to limit it to trigger at least 50ms. The following are the encapsulation throttling functions.
export default function(fn, delay) {
let lock = false;
return (. args) = > {
if (lock)
return;
// Enter the lock
lock = true;
setTimeout(() = > {
fn.apply(this, args);
// The account is unlocked
lock = false; }, delay); }}Copy the code
The complete code
<template>
<div class="list" @scroll="scrollHandle" ref="list">
<div class="item" v-for="(item,index) in renderList" :key="index" :style="`height:${itemHeight}px; line-height:${itemHeight}px; transform:translateY(${top}px)`">
{{item}}
</div>
</div>
</template>
<script>
import throttle from '@/utils/throttle';
export default {
name: 'App'.data() {
return {
list: [].// Complete list
itemHeight:60.// The height of each term
renderList: [].// The list to render
start:0.// Start rendering position
volume:0.// Page capacity: how many nodes can fit
top:0,
scroll,// Used to initialize throttling}},mounted() {
this.initList();
const cHeight=document.documentElement.clientHeight
// Compute page can hold several nodes and set up four nodes for redundancy
this.volume=Math.ceil(cHeight/this.itemHeight)+4;
// Set the list to render to the maximum number of elements it can hold
this.renderList=this.list.slice(0.this.volume);
// Initialize the throttling function to fire at least 50 milliseconds
this.scroll=throttle(this.onScroll,50);
},
methods: {
// Initialize the list with a loop of 500 entries
initList(){
for(let i=0; i<500; i++){this.list.push(i); }},scrollHandle(){
this.scroll();
},
onScroll(){
// The scrollTop constant records the height of the current scroll
const scrollTop=this.$refs.list.scrollTop;
const start=this.getCurStart(scrollTop);
// Compare the last start node to see if it has changed, and then re-render the list
if(this.start! =start){// We need to get a number divisible by itemHeight as the offset of the item
const offsetY = scrollTop - (scrollTop % this.itemHeight);
// Use slice to get the part that needs to be rendered
this.renderList=this.list.slice(start,this.start+this.volume);
TranslateY transform:translateY(${top}px)
this.top=offsetY;
}
this.start=start;
},
getCurStart(scrollTop){
// How many books are there
return Math.floor(scrollTop/(this.itemHeight)); }}},</script>
<style>* {margin: 0;
padding: 0;
}
.list{
height: 100vh;
overflow: scroll;
}
.item{
text-align: center;
width: 100%;
box-sizing: border-box;
border-bottom: 1px solid lightgray;
}
</style>
Copy the code
conclusion
There are mistakes welcome to point out, the whole article down pure handwriting, the first time to write a blog, please excuse me.