We all know that the advantage of Easyui's DataGrid component when loading large amounts of data is not obvious, compared to some other frameworks, if the amount of data reaches thousands of, it will be slower, especially under IE. In this case, the first thing we do is to optimize the various aspects of the DataGrid component performance, but anything can be solved, Virtualscrollview is a good solution.
Virtualscrollview's guideline is to draw less TR into table, the height of the table is limited, and the user's visible area is very limited, so the amount of data is very large, it is not necessary to draw all the data data into the table, resulting in a large dom, resulting in slower loading speed.
Source Analysis > Source Analysis
The DataGrid component of JQuery Easyui has also expanded a virtualscrollview view, and let's analyze its source code:
- var ScrollView = $.extend ({}, $.fn.datagrid.defaults.view, {render:function (target, container, frozen) {
- var state = $.data (target, ' DataGrid '); var opts = state.options;
- This place should pay special attention, not using the State.data.rows data//instead of the view.rows, and View.rows in the Onbeforerender event is set to undefined
- Onbeforerender event in ScrollView, even if the URL method will only be triggered once, so at the first rend, there is no data directly return. var rows = This.rows | | [];
- if (!rows.length) {return;
- } var fields = $ (target). DataGrid (' Getcolumnfields ', frozen);
- If it is rend frozen part, but there is no line number and frozencolumns, then return directly
- if (frozen) {if (!) ( Opts.rownumbers | | (Opts.frozencolumns && opts.frozenColumns.length))) {
- Return }
- }
- var index = This.index; var table = ["];
- for (var i=0; i<rows.length; i++) = "" "{=" "var=" "css=" opts.rowstyler "? =" "Opts.rowstyler.call (target,=" "index,=" " Rows[i]) = "": = "" "; =" "<li=" "class=" alt "> var classvalue ="; var stylevalue = ';
- if (typeof css = = ' string ') {stylevalue = CSS;
- } else if (CSS) {classvalue = Css[' class '] | | ‘‘;
- Stylevalue = css[' Style ' | | ‘‘; }
- var cls = ' class= ' datagrid-row ' + (index% 2 && opts.striped?) ' Datagrid-row-alt ': ') + Classvalue + ' "; var style = Stylevalue? ' style= ' + stylevalue + ' "': ';
- Get the class and style attributes for this row//var cls = (index% 2 && opts.striped)? ' class= ' Datagrid-row datagrid-row-alt ': ' class= ' datagrid-row ';
- var stylevalue = Opts.rowstyler? Opts.rowStyler.call (target, index, rows[i]): "; var style = Stylevalue? ' style= ' + stylevalue + ' "': ';
- var rowId = state.rowidprefix + '-' + (frozen?1:2) + '-' + index; Table.push ('
‘);
- Table.push (This.renderRow.call (this, target, fields, frozen, index, rows[i])); Table.push ('
‘);
- index++; }
- Table.push ('
‘);
- $ (container). HTML (Table.join (")); },
- /**
- * Onbeforerender event, first of all to understand two points: * 1-call loaddata method to load data data, LoadData internal rend before the event is triggered
- * 2-url mode, after acquiring the remote data, also uses the LoadData method to load the data, so the URL method will also trigger the Onbeforerender event * @param {dom} target DataGrid Instance host DOM Object
- * @return {[type]} [description] */
- Onbeforerender:function (target) {var state = $.data (target, ' DataGrid ');
- var opts = state.options; var dc = STATE.DC;
- var view = this; Delete the Onloadsuccess event to prevent it from being triggered and back up to State.onloadsuccess
- state.onloadsuccess = opts.onloadsuccess; opts.onloadsuccess = function () {};
- Opts.finder.getRow = function (t, p) {
- var index = (typeof p = = ' object ')? P.attr (' Datagrid-row-index '): p; var row = $.data (t, ' DataGrid '). Data.rows[index];
- if (!row) {//What situation will not be taken? var v = $ (t). DataGrid (' Options '). View;
- row = V.rows[index-v.index]; }
- return row; };
- Dc.body1.add (dc.body2). empty ();
- This.rows = undefined; The need to draw the TR bound to view.rows on the THIS.R1 = THIS.R2 = []; VIEW.R1 and VIWE.R2 each hold a reference to the first TR and the last page tr
- Do not take it for granted, just bound to the event, when the first load of data, when exactly when the event is triggered?//This problem goes back to the LoadData method, each time after loaddata will be directly used Triggerhandler trigger scroll
- Dc.body2.unbind ('. DataGrid '). Bind (' Scroll.datagrid ', function (e) {if (state.onloadsuccess) {
- opts.onloadsuccess = state.onloadsuccess; Recover onloadsuccess Event state.onloadsuccess = undefined;
- } if (View.scrolltimer) {//clear Timer
- Cleartimeout (View.scrolltimer); }
- Delay 50 ms Execution View.scrolltimer = setTimeout (function () {
- Scrolling.call (view); }, 50);
- });
- function Scrolling () {if (dc.body2.is (': Empty ')) {//dc.body2 corresponds to the normal column data, if empty, the description has no data.
- Attempt to load data without data reload.call (this);
- } else {var firsttr = Opts.finder.getTr (target, This.index, ' body ', 2);
- var lasttr = opts.finder.getTr (target, 0, ' last ', 2); var headerheight = Dc.view2.children (' Div.datagrid-header '). Outerheight ();
- var top = firsttr.position (). Top-headerheight; var bottom = Lasttr.position (). Top + lasttr.outerheight ()-headerheight;
- if (Top > dc.body2.height () | | Bottom < 0) {
- Reload.call (this); } else if (Top > 0) {
- var page = Math.floor (this.index/opts.pagesize); This.getRows.call (this, target, page, function (rows) {
- THIS.R2 = THIS.R1; THIS.R1 = rows;
- This.index = (page-1) *opts.pagesize; This.rows = This.r1.concat (THIS.R2);
- This.populate.call (this, target); });
- } else if (Bottom < Dc.body2.height ()) {//need to load next page in case var page = Math.floor (this.index/opts.pagesize) +2;
- if (this.r2.length) {page++;
- } this.getRows.call (this, target, page, function (rows) {
- if (!this.r2.length) {this.r2 = rows;
- } else {this.r1 = THIS.R2;
- THIS.R2 = rows; This.index + = Opts.pagesize;
- } this.rows = This.r1.concat (THIS.R2);
- This.populate.call (this, target); });
- } }
- function Reload () {
- var top = $ (dc.body2). scrolltop ();//rolled-up height var index = math.floor (TOP/25);//gets rolled-up row index, such as: Roll up a row and half 37.5,index for 1
- var page = Math.floor (index/opts.pagesize) + 1;//Gets the number of pages, if 10 per page, rolled 262.5,page to 2
- This.getRows.call (this, target, page, function (rows) {This.index = (page-1) *opts.pagesize;// View.index is the index of the first row of page pages
- This.rows = Rows;//view.rows to store the TR THIS.R1 required to be painted = rows;
- THIS.R2 = []; This.populate.call (this, target);
- Dc.body2.triggerHandler (' Scroll.datagrid '); });
- } }
- },
- Getrows:function (target, page, callback) {var state = $.data (target, ' DataGrid ');
- var opts = state.options; var index = (page-1) *opts.pagesize;
- var rows = State.data.rows.slice (index, index+opts.pagesize); if (rows.length) {//This is the way that all data is loaded at once, you can extract the data directly from the local JavaScript array
- Callback.call (this, rows);
- } else {//lazy load mode var param = $.extend ({}, Opts.queryparams, {
- Page:page, Rows:opts.pageSize
- }); if (opts.sortname) {
- $.extend (param, {sort:opts.sortName,
- Order:opts.sortOrder});
- } if (Opts.onBeforeLoad.call (target, param) = = false) return;
- $ (target). DataGrid (' loading ');
- var result = Opts.loader.call (target, param, function (data) {$ (target). DataGrid (' loaded ');
- var data = Opts.loadFilter.call (target, data); Callback.call (Opts.view, data.rows);
- Opts.onLoadSuccess.call (target, data); }, function () {
- $ (target). DataGrid (' loaded '); Opts.onLoadError.apply (target, arguments);
- }); if (result = = False) {
- $ (target). DataGrid (' loaded '); }
- } },
- Populate:function (target) {
- var state = $.data (target, ' DataGrid '); var opts = state.options;
- var dc = STATE.DC; var rowHeight = 25;
- if (this.rows.length) {
- Opts.view.render.call (Opts.view, Target, dc.body2, false); Opts.view.render.call (Opts.view, Target, dc.body1, true);
- See, the scroll bar has so much space is how to achieve it? With the padding! Dc.body1.add (Dc.body2). Children (' table.datagrid-btable '). CSS ({
- Paddingtop:this.index*rowheight, Paddingbottom:state.data.total*rowheight-this.rows.length*rowheight-this.index *rowheight
- }); Opts.onLoadSuccess.call (Target, {
- Total:state.data.total, Rows:this.rows
- }); }
- } });The Virtualscrollview principle is to simulate the maximum data volume by setting up the upper and lower padding of Div, We only draw a little more than the visible part of the Treasyui Virtualscrollview support two ways: one is to request all the data at once The second is that each time Ajax to pageSize data Easyui the Virtualscrollview of the TR number is 2*pagesize (the first load exception, when only painting 1*pagesize tr) Easyui The Virtualscrollview view of the row height is mandatory as 25px, if you set a non-25px row height, this view will not work properly because only the 2*pagesize tr, so we dategrid height can not be set to more than 2*25* PageSize pixels, more than the words will cause the visible area is left white use the LoadData method to load the data loaddata the parameter does not need the total property, as long as the rows array can be, total within the loaddata automatically calculated
For the previous points, we look at the source code in the comments I wrote, the foundation is poor, see a indefinitely on the line, the basis of good, the best to thoroughly study.
Existing bug request background dead loopIf it is a URL, the first time the data is not loaded, the background is constantly being requested. See the 146 line, if the callback function does not accept the rows, it should not trigger the Scorll event, because the scroll event will request background data, I have to add the conditions on the line:
- if (rows && rows.length > 0) {dc.body2.triggerHandler (' Scroll.datagrid ') ;
- Two requests background
URL mode, if the background returned data is not enough to fill the table height, the background will be repeated (note that this place only repeats the request once, different from the first bug). The reason for this problem is also very simple, in fact, this situation, the DataGrid is a bit large, but the background and only a few data caused by only a few data, and this batch of data is not enough to fill the table visual area height. Let's add 122 lines to the call of the GetRows method:
- if (this.rows.length = opts.pagesize) {This.get Rows.call (this, target, page, function (rows) {
- if (!this.r2.length) {this.r2 = rows;
- } else {this.r1 = THIS.R2;
- THIS.R2 = rows; This.index + = opts.pagesize;
- This.rows = This.r1.concat (THIS.R2);
- this.populate.call (this, target);});
-
This.rows is a group of rows that are currently drawn, and if rows are not pagesize large, then it is no longer necessary to request data.
Virtualscrollview is a good optimization method, will be applied more and more widely, Easyui Virtualscrollview view Support editor I have to try, estimates are not supported, Interested students can go to research.
JQuery Easyui Datagrid virtualscrollview View Simple analysis