Continuing with the previous article:Ajax gets a list of images, we will implement the classic JS case, lazy loading

In Web applications, the response speed of the system is very important to user experience, and its main influence comes from server database query and DOM rendering.

In the case of large amount of database query data, we can carry out paging with front-end paging or lazy loading optimization, and this article mainly talks about how to deal with the impact of backend millions of data on DOM rendering, of course, this is often asked by Dachang.

Before implementing the functionality, let’s take a look at lazy loading, which I implemented earlierLazy loadingLazy loading means that a small part is loaded first. When certain conditions are triggered (the bottom is touched, the visible area is touched, and the scrollbar height reaches a certain condition), lazy loading is carried out to solve the problem of user experience decline caused by slow loading of a large number of resources at the same time.

Here, I will use Ajax to obtain the picture list process, and transfer tens of thousands or even millions of picture information from the back end at one time (generally, the back end will do paging, which is very bad for the performance of the server), and the front end adopts bottom loading and paging to solve the DOM rendering problem.

So let’s do that

Two functions need to be implemented on the server side:

  • Select an item based on a finite array based on a random index.
  • The server receives the parameters sent by the front end and decides how many pieces of data to pass (this allows the front end to see the effect directly without modifying the back end).

First, create a new image path, a total of 5 images

const picList = ['1.jpg'.'2.jpg'.'3.jpg'.'4.jpg'.'5.jpg']
Copy the code

Write random number method, passing in the number n to generate random numbers between 1 and n

function random (count) {
    return Math.floor(Math.random() * count)
}
Copy the code

Write a function that generates a list of pictures, passing in the number n to generate a random array of pictures of length N

function createPicList (len) {
    let _list = []
    for(let i = 0; i < len; i ++) {
        _list.push(picList[random(picList.length)])
    }
    return _list
}
Copy the code

This value is then returned to the front end in the getList interface (with the front end parameters to make the length manageable)

res.write(JSON.stringify(createPicList(data.len)));
Copy the code

Finally, open Server and type in your browserhttp://127.0.0.1:1024/getList?len=10, and the server will return 10 random image addresses

Now that the server is done, let’s test the client by changing the Ajax parameter to 1000 and adding a timestamp to record the running time

    let date = new Date(),
        serverDate,
        domDate;
    utils.myAjax({
        url: baseUrl + '/getList'.method: 'get'.data: {len: 1000},// Pass the length of the list (how many images to load)
        timeout: 10 * 1000
    }).then((data) = > {
        serverDate = new Date()
        loadPic.create(loadBox, data)
        domDate = new Date(a)// Other problems are temporarily ignored
        console.log('Request received:', (serverDate - date) / 1000)// Back end speed + transfer speed etc. (units per second)
        console.log('DOM Render: ', (domDate - serverDate) / 1000)//DOM rendering speed (units per second)
    }).catch((err) = > {
        console.log(err)
    })
Copy the code

With 1000 images, both the loading speed and DOM rendering speed are significant

The number of images went up to 10,000, and the back end didn’t change much, but Dom rendering was noticeably slower



Increase the number of images to 100,000, boy, CPU and memory went straight up



In the end, I took a desperate shot at 1,000,000 images and almost cracked. The back end looked solid and DOM rendered for 70 seconds

That means we need to optimize on the front end with millions of data transfers in control on the back end

First, we introduce the wheel we built earlier: Publish/subscribe busEvent (optionally, use a callback function to notify the render view), Module.exports = eventbus.instance (); export default eventbus.instance ();

Then create a new LazyLoad class like this:

import eventBus
    from './eventBus.js'
import Utils
    from './utils.js'

const utils = new Utils()
export default class LazyLoad {
    constructor (pageConfig, ele, data) {
        Object.defineProperties(this, {
            // Paging configuration for front-end paging
            'pageConfig': {
                value: pageConfig,
                writable: true
            },
            // The element to listen for scrolling
            'ele': {
                value: ele,
                writable: false
            },
            // All data, image list
            'data': {
                value: data,
                writable: false}});this.init()
    }

    /** * Intercepts start to end data after paging the total list and notifies the render page *@param Val Intercepts data */
    set pagingData (val) {
        // Whether to clear the previous page
        this.pageConfig.clear ? (utils.clearEle(this.ele), this.ele.scrollTop = 0) : ""
        eventBus.emitEvent('lazyLoadStep', val)// Inform the rendering layer when the data is turned and pass the captured data
    }

    /* * Initialize the function */
    init () {
        this.scrollLoadPage(this.ele)
        this.nextPage()// Get the first page
    }

    /* * Page turning function, if the number of pages is greater than the total number of pages will be displayed */
    nextPage () {
        if(this.pageConfig.page > this.pageConfig.totalPage) {
            console.log('The end! ')
            return
        }
        this.pagingData = this.paging(this.pageConfig, this.data)
        this.pageConfig.page ++
    }

    /* * scroll events */
    scrollLoadPage (ele) {
        ele.addEventListener('scroll'.this.loadHandler.bind(this))}/* * Roll event callback */
    loadHandler (e) {
        if(
            e.target.scrollHeight - e.target.scrollTop <= e.target.clientHeight
        ) {
            this.nextPage()
        }
    }
    /* * @param pageConfig page configuration attribute (page: number of pages, pageSize; * @param data total data * @return data Data captured on the current page */
    paging (pageConfig, data) {
        let {
            page,
            pageSize
        } = pageConfig
        pageConfig.totalPage = Math.ceil(data.length / pageConfig.pageSize)
        let startIndex = page * pageSize - pageSize
        let endIndex = page * pageSize
        return data.slice(startIndex, endIndex)
    }
}
Copy the code

Add eventBus and lazyLoad to the HTML page and replace loadpic. create with new lazyLoad (pageConfig, loadBox, data). And put loadpic. create in asynchronous processing of eventBus to get the following code:

    import Utils
        from "./js/utils.js"
    import LoadPic
        from './js/loadPic.js'
    import LazyLoad
        from "./js/lazyLoad.js";
    import eventBus
        from './js/eventBus.js'

    let pageConfig = {
        page: 1./ / what page
        pageSize: 30.// Size per page
        clear: false// Whether to empty the previous page when turning the page
    }
    let baseUrl = 'http://127.0.0.1:1024'// Request an address
    let picPath = '/static/'// Static file path
    let utils = new Utils()
    let loadBox = document.querySelector('#loadBox')// Elements that need lazy loading
    let loadPic = new LoadPic()
    let lazyLoad;
    let serverDate=new Date(),// Server response time
        domDate;// View render time
    loadPic.baseUrl = baseUrl + picPath
    eventBus.onEvent('lazyLoadStep'.function (e) {// Listen for page turning and render the page
        domDate = new Date(a)// Initialize the view rendering time
        loadPic.create(loadBox, e)
        console.log('DOM Render: ', (new Date() - domDate) / 1000)//DOM rendering speed (units per second)
    })
    utils.myAjax({
        url: baseUrl + '/getList'.method: 'get'.data: {len: 1000},// Pass the length of the list (how many images to load)
        timeout: 10 * 1000// Request timeout
    }).then((data) = > {
        console.log('Request received:', (new Date() - serverDate) / 1000)// Back end speed + transfer speed etc. (units per second)
        lazyLoad = new LazyLoad(pageConfig, loadBox, data)// Initialize lazy loading
    }).catch((err) = > {
        console.log(err)
    })
Copy the code

Now that a simple Ajax request to the back end and get a list of images, and a lazy pagination of the list to load images is complete, let’s try it out



If you set the previous page in pageConfig to true, it will be lazy to see only the next page, but the benefit of doing so is to reduce the memory problems caused by the dom element overload

Finally, thank you for seeing the last of you, attachedThe source addressI hope it will be helpful.