In recent months, I have been developing a VUe-based data visualization analysis auxiliary application — DMap(Listen), a location-based big data analysis tool for data analysts and data scientists, aiming to improve the efficiency of data analysis, reduce the cost of acquiring multiple data for parallel analysis, and simplify the development process of large screen and data report. The UI component uses iView, the map visualization library uses inMap, and the server uses Node.js to build.

The core of DMap is service big data analysis, so performance optimization is a challenging problem when facing tens of thousands or even millions of data levels. Today I will take the optimization of a table rendering in the project as an example to expand the introduction.

In front-end development, it is very common to display data in tables. When there is a large amount of data, we usually use pagination. If the data volume is not too much, only two or three pages, we can get the full amount of data down and do simple pagination display in front. When the data volume goes up to a higher level, we need to ask the server for the data required for this page based on the number of pages. However, as an analysis tool to help big data visualization, DMap needs to display a full amount of data in the front end. In order to improve user experience, we decided to display tables without pagging or lazy loading, but with seamless scrolling like Excel.

In the Web, a long list of rendering performance problems have some mature scheme, form and a long list of similar, when rendering when the number of lines to achieve a certain amount, scroll will be caton, so we use virtual rendering solution, is to render the user can only see a small part of the regional data, and then calculated by scrolling display of data, And the height of the upper and lower placeholders.

So just to get a sense of what’s going on, let’s talk about the computational details.

First we need to listen for the scroll event of the outer container of the table (that is, the element that displays the scroll bar). The only thing we do in the Scroll event binding method is to get the scrollTop value of the current scrolling height of the outer container. All of our calculations, including the substitution of three table positions, the selection of table data, and the calculation of the height of the upper and lower placeholders, are related to scrollTop.

Here is the scroll event binding method:

    handleScroll (e) {
      const ele = e.srcElement || e.target;
      const { scrollTop, scrollLeft } = ele;
      this.scrollLeft = scrollLeft;
      this.scrollTop = scrollTop;
    }
Copy the code

Here we simply assign the values of scrollTop and scrollLeft to the corresponding values of the vUE instance. Then we use watch to monitor the changes of scrollTop and calculate the currentIndex of the table currently in the visible area if it is updated:

this.currentIndex = parseInt((top % (this.moduleHeight * 3)) / this.moduleHeight);
Copy the code

The top here is the updated value of this.scrollTop, and moduleHeight is the height of a single table, which we call a module.

Now that we have the currentIndex value, we can calculate the display position of the three tables and the amount of data to populate each table. We render the three tables using the render function, and we return the different order of render functions based on the value of currentIndex:

getTables (h) {
	let table1 = this.getItemTable(h, this.table1Data, 1);
	let table2 = this.getItemTable(h, this.table2Data, 2);
	let table3 = this.getItemTable(h, this.table3Data, 3);
	if (this.currentIndex === 0) return [table1, table2, table3];
	else if (this.currentIndex === 1) return [table2, table3, table1];
	else return [table3, table1, table2];
}
Copy the code

The order of the table in the array is different, and the effect on the page is different order. Finally we get the full render using this method:

	renderTable (h) {
      return h('div', {
        style: this.tableWidthStyles
      }, this.getTables(h));
    }
Copy the code

We then use the encapsulated stateless component to render our render table.

<render-dom :render="renderTable"></render-dom>
Copy the code

The renderDom component is implemented as follows:

export default {
  name: 'RenderCell',
  functional: true,
  props: {
    render: Function,
    backValue: [Number, Object]
  },
  render: (h, ctx) => {
    returnctx.props.render(h, ctx.props.backValue, ctx.parent); }};Copy the code

Next we will talk about the calculation of the data filled in the next three tables.

We use scrollTop and currentIndex to calculate how many rounds each module is currently in. But since we started this logic from the second table (to make the rotation smoother), Therefore, it is necessary to determine whether the current scrolling height is greater than the height of a module. If so, the following calculation is performed:

switch (this.currentIndex) {
   case 0: t0 = parseInt(scrollTop / (this.moduleHeight * 3)); t1 = t2 = t0; break;
   case 1: t1 = parseInt((scrollTop - this.moduleHeight) / (this.moduleHeight * 3)); t0 = t1 + 1; t2 = t1; break;
   case 2: t2 = parseInt((scrollTop - this.moduleHeight * 2) / (this.moduleHeight * 3)); t0 = t1 = t2 + 1;
}
Copy the code

After calculating the number of rounds of display for each module, we can take the corresponding table data:

const count1 = this.times0 * this.itemNum * 3;
this.table1Data = this.insideTableData.slice(count1, count1 + this.itemNum);
const count2 = this.times1 * this.itemNum * 3;
this.table2Data = this.insideTableData.slice(count2 + this.itemNum, count2 + this.itemNum * 2);
const count3 = this.times2 * this.itemNum * 3;
this.table3Data = this.insideTableData.slice(count3 + this.itemNum * 2, count3 + this.itemNum * 3);
Copy the code

That’s all the important content of virtual rendering. After the development of the form, when it was actually used in the project, when loading more than 200,000 pieces of data to test, the whole page stuck unbearably, and the larger the data amount, the more serious the page stuck. There is no problem with our form. The problem is that Vue helped us “reverse work”.

For objects added to a Vue instance, Vue first iterates through all the attributes of the Object, creating getters and setters for each Object with Object.defineProperty(). In our project, our insideTableData is just a property in a data set object that also contains a lot of information about that data set, We are using this. InsideTableData. Slice data will trigger this. InsideTableData corresponding getter, to perform some other logic, Our scrolling will be frequent (only when currentIndex changes) and the table data will need to be repopulated, so this will cause a lag.

The solution is to prevent Vue from setting setters and getters for our cube objects. My solution is to use ES5’s Object.preventExtensions to seal the cube objects before handing them to the Vue instance agent. The dataset object becomes unextensible, Vue can’t add new properties, and setters and getters can’t be set.

After doing this, rendering hundreds of thousands of data is as smooth as a lark. However, preventing Vue from setting getters and setters also caused some problems. For example, some calculation properties in the original table component that depended on the table data could not be recalculated when the table data changed. Of course, the impact was minor, just the number of rows in a table, so it was changed to manually setting this value.

The vue-Bigdata-table table component that I packaged is not only capable of this, but also currently includes dragging to modify column width, fixing column not scrolling horizontally, Fixed table header, built-in sorting, editing cells, paste, filter, custom table header and cells and other functions. It is now open source, but there are still many features in development.

To play the transfer of Vue technology stack development, from the creation of the project to the project deployment online, you can watch my latest video tutorial: “Vue technology stack development Actual Combat”.

Github link: github.com/lison16/vue…

Welcome to Star.