Client-side pagination with 120K items

On 2020/08/25 at 00:04

When developing global ranking feature for the leetcode ranking search project, I have to deal with the 120K users' ranking data. Since I don't want to maintain a server, I would like the ranking search website to be fully static which means I have to deal with these 120K users' data in client side purely.

I used BootstrapVue as my frontent component framework and it has a <b-table> component for the table component which also has built-in client-side pagination feature to deal with data. However when loading 120K items into the table component, it makes the UI stop responding to anything!!

The problem is the <b-table> component generates all the 120K DOM elements when loading the data and it would cause the UI freeze when updating the DOM. To conquer this problem, I leverage the provider functions feature which is originally designed for implementing server-side pagination.

In the provider function I fetch the data only once and slice and filter the data each time the table component calls the provider function to retrieve the data for a specific page. In this way, the browser only generate a page of DOM elements which is only 25 elements instead of 120K elements. So the UI can function smoothly and responsive!!

rankProvider: function(ctx) {
    if (this.rank.length === 0) {
        return axios.get('data/global-ranking.json')
                    .then(function (resp) {
                    this.rank = resp.data;
                    this.filteredRank = this.rank;
                    this.totalRows = this.rank.length;
                    let start = (this.currentPage - 1) * this.perPage;
                    return this.rank.slice(start, this.perPage);
                    }.bind(this));
    } else {
        if (this.lastFilter != ctx.filter) {
            this.currentPage = 1;
            ctx.currentPage = 1;
            this.filteredRank = this.rank.filter(this.filterFunc);
            this.totalRows = this.filteredRank.length;
            this.lastFilter = ctx.filter;
        }
        let start = (ctx.currentPage - 1) * this.perPage;
        return this.filteredRank.slice(start, start + this.perPage);
    }
}

Comments