easyui datagrid加载数据只有两种方式:一种是ajax加载目标url返回的json数据;另一种是加载js对象,也就是使用loadDate方法。

这里就自己的使用经验,对两种方式做简单总结和归纳,并且对使用过程中容易产生的误区做较为详细的描述,希望能对大家有所帮助。

url方式加载数据

调用方式

目前可能大多数人都是选择这种方式,因为跟流行的框架结合的也比较好,使用url的话,可以将url写在DOM里面或者申明datagrid对象的url属性,以下两种方式都是可以的:

<table id="tt" style="width:700px;height:auto" title="DataGrid" idField="itemid" url="datagrid_data2.json"> 
 
$('#test').datagrid({   
    url:'datagrid_data2.json'   
});

 

相关方法

load

param

加载第一页数据,param将代替默认查询参数,注意的是该方法只适用于url方式.

reload

param

刷新当前页数据,与load方法不同的时候reload方法刷新当前页数据,而load方法会跳到第一页然后刷新.

options

null

获取datagrid实例的各项参数值,常用的参数有url,pageNumber,pageSize这三个参数在请求数据以及分页功能中起重要作用.

二次加载问题

对于使用url方式的初学者,经常碰到重复请求的问题,这个问题的根源是多次渲染组件,如何避免二次加载这样问题呢,个人觉得注意以下两点基本就可以防止二次加载了。

  • 使用load和reload函数去动态加载数据,而不是选择再次渲染组件。很多人再次渲染组件的目的仅仅是为了设置url,这得不偿失,url的设置可以通过options方法获取到组件实例的opts,然后在给opts.url重新赋值即可;
  • class方式注册组件和javascript注册方式不要同时使用。class注册方式一般是为了初始化属性,javascript方式则属性和事件都可初始化,但是不管是class方式还是javascipt方式注册组件,每次注册,只要被设置过url属性就会做请求。所以在不可避免要使用javascript方式注册的情况下,索性就不要使用class方式注册了。

因为url方式网上的资料特别多,我这里就简述这么多,下面重点讨论一下loadDate方式加载数据。

加载本地数据方式

首先要明白“加载本地数据”是个什么概念,这里指的是加载javascript对象数据,而javascript数据对象显然可以使是通过其它异步方式获得的,所以这个“加载本地数据”的描述并不准确。

调用方式

先要将url属性设置为null,或者不设置,然后使用datagrid的loadDate方法加载js数据对象,这个对象包含两个属性,一个是记录总数,一个是当前页码的对象数组。例如:

var obj = {'total':100,'rows':[{id:'1',name:'一'},{id:'2',name:'二'}]};   
$('#tt').datagrid('loadData',obj);

 

如何分页

不对源码做任何改动的话,可以首先获取datagrid的Pagination对象,然后通过写Pagination对象的onSelectPage事件来实现分页:

//初始化dategrid   
$('#tt').datagrid({   
    url:null,   
    pagination:true,   
    pageSize:20,   
    pageNumber:1,   
    rownumbers:true  
});   
$('#tt').datagrid('getPager').pagination({   
    displayMsg:'当前显示从 [{from}] 到 [{to}] 共[{total}]条记录',   
    onSelectPage : function(pPageIndex, pPageSize) {   
        //改变opts.pageNumber和opts.pageSize的参数值,用于下次查询传给数据层查询指定页码的数据   
        var gridOpts = $('#tt').datagrid('options');   
        gridOpts.pageNumber = pPageIndex;   
        gridOpts.pageSize = pPageSize;     
        //定义查询条件   
        var queryCondition = {name:"世纪之光"};   
        //异步获取数据到javascript对象,入参为查询条件和页码信息   
        var oData = getAjaxDate("orderManageBuz","qryWorkOrderPaged",queryCondition,gridOpts);   
        //使用loadDate方法加载Dao层返回的数据   
        $('#tt').datagrid('loadData',{"total" : oData.page.recordCount,"rows" : oData.data});   
    }   
});

 

上面的代码应该很容易看懂了,做出来的分页基本也算正常,唯一的缺憾就是写起来不怎么便捷。那么如何才能便捷地实现分页呢?

参照jQuery easyui datagrid 非URL后台分页的文章,稍微对easyui datagrid做下扩展,增加一个doPagination事件,那么编码就较为简单了。

//初始化dategrid   
$('#tt').datagrid({   
    url:null,   
    pagination:true,   
    pageSize:20,   
    pageNumber:1,   
    rownumbers:true,   
    doPagination:function(pPageIndex, pPageSize) {    
        var gridOpts = $('#tt').datagrid('options');   
        //定义查询条件   
        var queryCondition = {name:"世纪之光"};   
        //异步获取数据到javascript对象,入参为查询条件和页码信息   
        var oData = getAjaxDate("orderManageBuz","qryWorkOrderPaged",queryCondition,{pageNumber:gridOpts.pageNumber,pageSize:gridOpts.pageSize});   
        //使用loadDate方法加载Dao层返回的数据   
        $('#tt').datagrid('loadData',{"total" : oData.page.recordCount,"rows" : oData.data});   
    },   
});

 

这种方式就不用再去获取Pagination对象了,而且也不用设置opts的pageNumber和pageSize这两个属性了,编码变得简易了,是不是变得清爽了很多呢?

加载中效果

easyui datagrid只有在使用url方式获取数据的时候才会显示“加载中……”的遮罩效果,使用loadDate方法加载数据的话,其实也可以用上这效果,只不过稍微麻烦些:

//初始化dategrid   
$('#tt').datagrid({   
    url:null,   
    pagination:true,   
    pageSize:20,   
    pageNumber:1,   
    rownumbers:true,   
    doPagination:function(pPageIndex, pPageSize) {   
        //改变opts.pageNumber和opts.pageSize的参数值,用于下次查询传给数据层查询指定页码的数据   
        var gridOpts = $('#tt').datagrid('options');   
        gridOpts.pageNumber = pPageIndex;   
        gridOpts.pageSize = pPageSize;     
        Exec_Wait('tt','loadDateGrid()');   
    },   
});   
function loadDateGrid(){   
    var gridOpts = $('#tt').datagrid('options');   
    //定义查询条件   
    var queryCondition = {name:"世纪之光"};   
    //异步获取数据到javascript对象,入参为查询条件和页码信息   
    var oData = getAjaxDate("orderManageBuz","qryWorkOrderPaged",queryCondition,gridOpts);   
    //使用loadDate方法加载Dao层返回的数据   
    $('#tt').datagrid('loadData',{"total" : oData.page.recordCount,"rows" : oData.data});   
}   
  
/**
 * 封装一个公用的方法  
 * @param {Object} grid table的id  
 * @param {Object} func 获取异步数据的方法  
 * @param {Object} time 延时执行时间  
 */  
function Exec_Wait(grid,func,time){   
    var dalayTime = 500;   
    __func_=func;   
    __selector_ = '#' + grid;   
    $(__selector_).datagrid("loading");   
    if (time) {   
        dalayTime = time;   
    }   
    gTimeout=window.setTimeout(_Exec_Wait_,dalayTime);   
}   
function _Exec_Wait_(){   
    try{eval(__func_);   
    }catch(e){   
        alert("__func_:" + __func_ + ";_ExecWait_" + e.message);   
    }finally{   
        window.clearTimeout(gTimeout);   
        $(__selector_).datagrid("loaded");   
    }   
}

 

当然了,你也可以利用datagrid的onLoadSuccess事件来实现,反正最终都是调用datagrid的loding和loaded方法完成等待DIV的显示和隐藏的:

function loadDateGrid(){   
    $('#tt').datagrid('loading');//打开等待div   
    var queryCondition = {   
        name: "世纪之光"  
    };   
    var oData = getAjaxDate("orderManageBuz", "qryWorkOrderPaged", queryCondition, oPage);   
    $('#tt').datagrid('loadData', {   
        "total": oData.page.recordCount,   
        "rows": oData.data   
    });   
}   
  
$('#tt').datagrid({   
    onLoadSuccess: function(){   
        //加载完数据关闭等待的div   
        $('#tt').datagrid('loaded');   
    }   
});

 

如何不统计总数

有时候数据层的数据量特别大,查询统计总数的话会很耗时,统计总数就显得不怎么合适了,如何不统计总数完全看你后台怎么写了,可以虚拟一个一个总数,这个数字总是比当前页码大1就行了,具体实现就不在本篇文章关注的范围内了。

到这里,loadData方式加载grid数据就已经算是完美了。

EasyUI Datagrid性能优化

jQuery  EasyUI的Datagrid组件功能算是很强大了,不过性能确实不怎么乐观,而对于性能问题,网络上几乎也找不到相关的优化资料,所谓的牛人们可能都望而却步了。本博客以后会带着分析Datagrid组件的性能问题,并且给出优化方案,也希望大家能集思广益,给出一些好的想法。

慢在哪些方面

以目前对Datagrid的了解程度去看待性能问题,主要有以下几点:

  • 加载大数据量时比较慢(不考虑服务端返回数据的时间),这点尤其体现在IE浏览器里面;
  • 大数据量时,加载后,操作很不流畅,勾选慢,singleSelect为true的话点选也比较慢,IE浏览器也是尤其突出;
  • 数据量一般,但是字段特别多的话,加载和操作也比较慢,当然了,这种情况比较少见;
  • 可编辑表格的性能则是更为糟糕,数据量达到几十条的时候,操作就会相当不流畅,IE依旧很突出

大数据量的加载

原因分析

不考虑服务端返回数据的时间,在前台获取到大数据量后,往表格里插入tr的时候,IE执行的效率非常低,2000条数据要45秒左右,其他浏览器则很快。

通过单步调试发现,默认视图在最后将tr写到table里面用的是jQuery的html()函数,就是这个函数在IE下执行效率非常低。

解决方案一:返璞归真

jQuery是个很锋利的工具,可有时候我们也得返璞归真一下,为什么非要用jQuery的html()函数呢,我们就用javascript dom对象里面的innerHtml属性不就可以了么,而且换成innerHTML属性方式的话,效率提高几十倍。

所以,大数据量加载慢的问题,就这么简单就解决了,修改默认视图render方法最后那句:

//1.3.3版本是这样的,其它版本也是这句代码
$(_1e0).html(_1e4.join(""));

 

改为:

$(_1e0)[0].innerHTML = _1e4.join("");

 

注意:innerHTML虽然符合w3c标准,而且各个浏览器也都支持,但是表现出的行为却又差异,另类的浏览器依旧是IE,主要表现在以下几个方面:

  • IE6,IE7,IE8浏览器设置innerHTML属性会忽略html5属性和标签,搜索关键词"innerHTML IE html5";
  • IE几乎所有版本设置innerHTML属性时都会把href,src属性自动转化为绝对路径,搜索关键词"innerHTML IE href";
  • IE几乎所有版本的table相关标签的innerHTML属性是只读的(td除外),搜索关键词"innerHTML IE table;

幸运的是EasyUI的datagrid默认视图没有使用html5技术,调用innerHTML的节点也并非table节点(是div),而href,src等转化为绝对路径并没有什么影响。

解决方案二:使用scroll视图

VirtualScrollView视图官网已经写出来了,不过有两个Bug而已,对这个视图的源码也分析过,请大家参照:

http://www.easyui.info/archives/1404.html

勾选和点选

原因分析

勾选和点选(开启singleSelect)慢的原因其实是一样的,都是选择器执行效率低,这里我拿勾选的情况来分析。

具体的分析过程我就不描述了,知道用chrome,fireBug,IE开发者工具调试的同学,应该都有定位问题的思路:先定位执行效率低的函数,再在函数内定位执行效率低的语句。

checkbox导致操作不流畅的原因,我最后定位到opts.finder.getTr这个方法上,我们来看它的代码片段:

if (type == "checked") {
    return (_21d == 1 ? dc.body1 : dc.body2).find(">table>tbody>tr.datagrid-row:has(div.datagrid-cell-check input:checked)");
}

 

这段代码是获取已经被勾选的rows,大家可以看到,这是纯粹的jQuery选择器查询,效率就慢在has这个伪选择器上,它是针对所有后代元素的,查找的效率是比较慢的,又是在这么多数据量的情况下,其效果就可想而知了。

优化方案一:选择器优化

其实对于checkbox列的DOM结构是固定的,我们完全可以用速度快的选择器来代替":has",我们先直接用路径选择器找到"input:checked",然后使用三次parent()函数返回tr,写法虽然复杂了,但是效率应该提高一点,所以我们改成这样:

if (type == "checked") {
    return (_21d == 1 ? dc.body1 : dc.body2).find(">table>tbody>tr.datagrid-row>td>div.datagrid-cell-check>input:checked").parent().parent().parent();
}

 

我用自己的服务大概测试了修改前后的效率(jQuery版本1.8.0,EasyUI版本1.3.3,singleSelect为false,2000条数据勾选一条记录的测试情况):


浏览器

执行时间

浏览器

执行时间

原版

IE9

600ms

chrome

60ms

选择器优化

IE9

560ms

chrome

60ms

从上面的结果可以看出,在这种测试条件下,我们提高的效率并不大,IE9下提高的效率尽管有所提高,但是还是很不理想,而chrome下性能基本一样。测试过程中发现,如果使用jQuery2.0的话,IE9下的执行时间将达到45000ms,几乎让人奔溃,看来尽管IE9勉强支持jQuery2.x,但是效率很挫。

优化方案二:实时记录优化法

既然慢在DOM结构巨大时,jQuery选择器的搜索效率不是很好(特别是在IE下)。如果我们每次操作都记录下勾选的tr,那么就完全可以绕开选择器。

具体该怎么做呢,我们给$.data(target,'datagrid')变量增加两个属性:"checkedTrsBody1"和"checkedTrsBody2"分别存储frozen部分和normal部分被勾选tr的引用,然后在各个设计到勾选的操作中维护这两个属性。最后,获取被勾选tr的时候就可以直接从这两个属性中取了,其耗时是可以忽略的。

那么究竟哪些操作会影响到被勾选的tr呢,我们罗列一下,也就以下几种:"checkRow","uncheckRow","uncheckAll","checkAll","deleteRow","loadData","load","reload".我们只要在这些接口中维护起"checkedTrsBody1"和"checkedTrsBody2"属性就可以了。

至于具体的代码怎么改,请参照:

http://www.easyui.info/version/jquery-easyui-1.3.3/plugins/jquery.datagrid.js

数据报表统计

勾选性能测试【IE9;jQuery-1.8.0;EasyUI-1.3.3;singleSelect:false】:

 

优化执行时间(ms)

原版执行时间(ms)

200条

7

64

500条

20

160

1000条

27

308

2000条

53

623

4000条

107

1323

6000条

192

2072

8000条

265

2865

10000条

331

3611

可以看出来,无论是在IE9下,勾选效率都提高了很多倍(chrome下效率也有显著提高)。开启singleSelect的优化思路是一样的,所以不写重复文字了。

渲染性能测试【IE9;不考虑服务器响应时间】:

 

优化渲染时间(ms)

原版渲染时间(ms)

200条

49

326

500条

122

1821

1000条

253

7002

2000条

525

27320

4000条

1083

110115

6000条

1683

200000

8000条

2261

200000

10000条

2900

200000

原版的datagrig,我本地的测试环境数据在4000条以上时,IE9基本就卡死了(可能机器性能不太好),无统计价值了,即便是4000条数据,也要将近2分钟才渲染完,显然没人能够忍受。

从报表很明显可以看出优化过的表格,即便是10000条数据,3秒也就渲染完成了。

优化演示

未优化版本:http://www.easyui.info/version/jquery-easyui-1.3.3/demo/datagrid/bigdata_checkbox.html

优化版本:http://www.easyui.info/version/jquery-easyui-1.3.3/demo/datagrid/bigdata_checkbox_optimized.html