跳转到主要内容

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

所有提交的信息确保安全。

  • 关闭 [x]

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

所有提交的信息确保安全。

  • 关闭 [x]

提升 Dojo TreeGrid 性能

以较少的时间加载大量数据

Xu Sheng Hai, 软件工程师, IBM
//www.ibm.com/developerworks/i/p-shaixu.jpg
Sheng Hai Xu 是上海 IBM China Development Lab 的 Globalization Development 小组的一名软件工程师。他在使用 Dojo 和 Web 2.0 技术开发基于 web 的工具方面有着丰富的经验。
Yang Liu, 软件工程师, IBM
http://www.ibm.com/developerworks/i/p-yliu.jpg
Yang Liu 是位于中国上海的 IBM China Software Development Lab 的一名软件工程师,她在上海 Globalization Lab 工作,致力于 RFID 技术、 Web 服务和全球化技术的研究。
Jie Hu, 软件工程师, IBM
http://www.ibm.com/developerworks/i/p-jhu.jpg
Jie Hu 是上海 IBM China Development Lab 的 Globalization Development 小组的技术主管。他有 6 年的 J2EE 开发经验。

简介: Dojo TreeGrid 是一个很有的小部件,用于在一个 web 页内呈现分层数据。然而,在处理大数据集时 TreeGrid 非常的慢。在本文中学习如何通过定制 TreeGridQueryStore 来缓解这一问题。

发布日期: 2011 年 11 月 28 日
级别: 中级 原创语言: 英文
访问情况 : 5084 次浏览
评论: 


简介

如果您想要在一个 web 页面上呈现分层数据,Dojo TreeGrid 是一很有用的小部件。 但是如果您有一个大数据集,那么 TreeGrid 速度将极其缓慢。在本文中学习如何通过定制 TreeGridQueryStore 缓解这一问题。本文描述了您在使用这两个小部件时可能遇到的各种问题,解释了这些错误形成的原因,然后帮助您创建一个解决方案。您可以 下载 本文所用的样例代码。


Dojo 网格和大数据集

当然,您可以使用网格在浏览器中有效地显示相对较小的数据集。您可以获得精确的排序、列大小调整等等。然而,实际上对您在一定时间内可以处理的记录数量是有限制的,这最终将导致页编码问题。

您可以忘记分页了 — 那些日子已经一去不返了。Dojo 网格使用延迟加载 DOM 渲染(lazy DOM rendering)作为网格卷轴。对于一个只有几百条记录的相对较小的数据集,当您滚动时懒惰 DOM 渲染为预加载的数据集增建 DOM。例如,如果您有 100 条记录,但是每次只能查看 20 条,您不需要为第 20 至 100 条记录构建节点,除非您滚动到某一条。按一列或者相关任务进行排序在内存中像小数据集那样运行;您使用 JavaScript 就可以有效地完成任务。

对于非常庞大的数据集,您很快就会发现使用 JavaScript 执行任务(比如按某列进行排序)将变得不切实际,甚至不可行。如果数据集非常庞大,试图在浏览器中使用一个 ItemFileReadStore 对其进行维护是不切实际的,因为这需要将所有数据下载到浏览器中。

Dojo 处理大数据集的方法简洁精致,可归结为以下两个方面:

  • 一个 dojo.data 实现可以以任意页面大小从服务器请求数据。本文使用 QueryReadStore
  • 一个有滚动能力的网格,它可以根据需要请求和加载一个特定的页面。

本文余下部分将向您介绍如何使用 QueryReadStore 构建一个延迟加载(lazy-loaded)树型网格。


QueryReadStore、TreeGrid 和 TreeModel

本小节提供了 QueryReadStoreTreeGridTreeModel 的基础介绍。

QueryReadStore

正如 dojocampus.org doc 文档所述(见 参考资料),QueryReadStoreItemReadStore 非常相似。它们都是用 JSON 作为它们的交换格式。不同之处是它们请求数据的格式。ItemReadStore 从服务器进行一次获取,然后处理客户端的所有排序和过滤。如果是几百条记录就没有什么问题,但如果是几十万条记录,或者是较慢的 Internet 连接,ItemReadStore 就不怎么可行了!

QueryReadStore 针对每个排序或查询向服务器发出请求,使其成为带有小窗口数据的大数据集的理想之选,正如 dojox.grid.DataGrid。清单 1 列出了如何创建一个 QueryReadStore


清单 1. 创建一个 QueryReadStore
				
var url = "./servlet/QueryStoreServlet";
var store = new dojox.data.QueryReadStore({
    url: url,
    requestMethod: "get"
});
            

TreeGrid 和 TreeModel

一个树型小部件,比如 TreeTreeGrid,显示一个分层数据视图。TreeGrid 小部件本身只不过是该数据的一个视图,真正的力量来自于 TreeModel,它呈现 Tree 小部件将要显示的实际分层数据。

通常,数据最终来自一个数据存储库,但 Tree 小部件与一个 dijit.tree.Model(匹配此树所需的方法的某个 API 的一个对象 )连接。因此,Tree 小部件可以以各种格式访问数据,比如一个数据存储,其中条目引用它们的父节点。

TreeModel 负责某些任务,比如连接到数据源、延迟加载、以及从 Tree 小部件查询条目和条目层次结构。例如,一个任务也能获取这个 Tree 小部件的一个子条目。TreeModel 还负责将 Tree 的更改通知给该数据。

开始之前,您需要定义 ForestStoreModelTreeModel 的一个实现)的查询来返回 TreeGrid 的多个顶级条目。您也将创建树的模型适配器来访问存储库。您需要清单 2 中的参数来构建一个 TreeModel


清单 2. 创建 TreeModel 的代码
				
var query = { 
    type: "PARENT" 
}; 

var treeModel = new dijit.tree.ForestStoreModel({ 
    store: store, // the data store that this model connects to 
    query: query, // filter multiple top level items 
    rootId: "$root$", 
    rootLabel: "ROOT", 
    childrenAttrs: ["children"], // children attributes used in data store. 
    /* 
      For efficiency reasons, Tree doesn't want to query for the children 
      of an item until it needs to display them. It doesn't want to query 
      for children just to see if it should draw an expando (+) icon or not. 
      So we set "deferItemLoadingUntilExpand" to true. 
    */ 
    deferItemLoadingUntilExpand: true 
}); 
            

QueryReadStore、TreeGrid 和 TreeModel 之间的关系

要了解如何使用一个 TreeGrid,注意以下相互依赖的树组件:

  • QueryReadStore 负责从服务器获取数据。
  • ForestTreeModel 负责从 QueryReadStore 查询条目的层次结构,并通知 TreeGrid 更新。
  • TreeGrid 负责显示数据,且只处理用户事件。

图 1 显示了这 3 个组件之间的关系。清单 3 显示了如何创建一个树。


图 1. QueryReadStore、TreeGrid 和 TreeModel 之间的关系
Flowcart 显示从服务器到用户活动的步骤,queryreadstore、foresttreemodel 和 treegrid 作为步骤

清单 3. 创建一个 Tree
				
// define the column layout for the tree grid. 
var layout = [ 
    { name: "Name", field: "name", width: "20%" }, 
    { name: "Age", field: "age", width: "auto" }, 
    { name: "Position", field: "position", width: "auto" }, 
    { name: "Telephone", field: "telephone", width: "auto" } 
]; 

// tree grid 
var treegrid = new dojox.grid.TreeGrid({ 
    treeModel: treeModel, 
    structure: layout, // define columns layout 
    /* 
      A 0-based index of the cell in which to place the actual expando (+) 
      icon. Here we define the "Name" column as the expando column. 
    */ 
    expandoCell: 0, 
    defaultOpen: false, 
    columnReordering: true, 
    rowsPerPage: 20, 
    sortChildItems: true, 
    canSort: function(sortInfo) { 
        return true; 
    } 
}, "treegrid"); 

treegrid.startup(); 


QueryReadStore 与 TreeGrid 如何协同工作

本小节介绍使用 TreeGrid 渲染数据、使用 QueryReadStore 进行排序以及展开父节点。

渲染

QueryReadStore 将发送面向顶级节点的请求到服务器,Store 向服务器发送的第一个请求将是面向顶级节点(根节点的子节点)的一个请求。这个 dojo.data 请求遵循一个特定的 JSON 格式。

树型网格将使用提供给模型的查询发出初始请求,Store 将它和目标结合起来。清单 4 显示请求格式、REST URL 模式、以及服务器对请求的响应。在这个实例中,请求是 REST 模式的。


清单 4. 查询请求和服务器响应
				
// query 
{ 
  query: {type: "PARENT"}, 
  start: 0, 
  count: 20 //rowsPerPage attribute of the tree grid 
} 

// request sample 
url?type=PARENT&start=0&count=20 

// server response 
{ 
  "identifier": "id", 
  "label": "name", 
  "items": [ 
    { "id": "id_0", 
      "name": "Edith Barney", 
      "age": 39, 
      "position": "Marketing Manager", 
      "telephone": 69000044 
      "children": true, 
      "type": "PARENT" 
    }, 
    { "id": "id_1", 
      "name": "Herbert Jeames", 
      "age": 43, 
      "position": "Brand Executive Manager", 
      "telephone": 69000077, 
      "type": "Child" 
    }, 
    ... 
  ], 
  "numRows": 10000 // total records in the grid 
} 
            

"Edith Barney" 条目是 "PARENT" 类型,"Herbert Jeames" 条目类型是 "Child",树模型中的查询是 {type: "PARENT"}, 因此,树型网格最初只显示 "Edith Barney" 记录。

事实上,您不需要包含子节点;一个 children 属性的存在就表明 Tree 的节点是可展开的,展开图标将被包括在内。条目 "Edith Barney"children 属性不是一个假值,因此,树模型将其视为一个有子节点的节点。在树型网格的 Edith Barney 行有一个加(+)号。如图 2 所示。


图 2. 渲染已创建 TreeGrid 的结果
显示名称、年龄、工作职位、职务、电话号码,其中几个名字带一个 + 号。

现在您已经在 TreeGrid 中渲染了这些数据,您想要证实是否 TreeGrid 特性(比如排序和展开父节点)能与 QueryReadStore 正常运作。

排序

要试用排序功能,单击 Name 列标题,根据名称升序排列。其他列的升序或降序排序也相同。


清单 5. 排序
				
// the tree model will pass a request to the query store like this: 
{ 
  query: {type: "PARENT"}, 
  start: 0, 
  count: 20, 
  sort: { 
    attribute: "name", 
    descending: false 
  } 
} 

// The query store will request server like this: 
url?type=PARENT&start=0&count=20&sort=name 
            

图 3 显示了排序结果.


图 3. 排序结果
显示名称、年龄、工作职位、职务、电话号码,其中几个名字带一个 + 号,按字母顺序排序的

展开父节点

对于较大的树型数据集,您可能只想加载可见树节点的必要数据,当用户展开一个节点后再记载该节点的子节点。理想情况下,每个扩展您只需发出一个 HTTP 请求,实现性能最优化。

在本例中,当用户点击其中一个节点时,树将请求存储库加载条目,存储库将请求资源。但有可能出现不可预料现象,比如在我们的示例中,当加号图标消失了,但子条目并没有显示出来。


图 4. 不能正确展开
显示名称、年龄、工作职位、职务、电话号码。一行没有 + 号也没有展开。

为什么不能运行呢?

仔细查看清单 6 中的 TreeGridTreeModel 源代码,看看如何展开子条目。


清单 6. TreeGrid 和 TreeModel 如何展开子条目
				
//TreeGrid.setOpen
setOpen: function(open){
    ...

    treeModel.getChildren(itm, function(items){
        d._loadedChildren = true;
        d._setOpen(open);
    });

    ...
} 
//TreeModel.getChilren
getChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ onComplete,
/*function*/ onError){
    // summary:
    // Calls onComplete() with array of child items of given parent item, all loaded.

    var store = this.store;
    if(!store.isItemLoaded(parentItem)){
    // The parent is not loaded yet, we must be in deferItemLoadingUntilExpand mode,
    // so we will load it and just return the children (without loading each child item) 
        var getChildren = dojo.hitch(this, arguments.callee);
        store.loadItem({
            item: parentItem,
            onItem: function(parentItem){
                getChildren(parentItem, onComplete, onError);
            },
            onError: onError
        });
        return; 
    }
    // get children of specified item
    var childItems = [];
    for(var i=0; i<this.childrenAttrs.length; i++){
        var vals = store.getValues(parentItem, this.childrenAttrs[i]);
        childItems = childItems.concat(vals);
    }
}

树模型首先请求存储库加载父条目。父条目不能被完全加载,因为其 children 属性不是一个列出所有子条目的数组。

然而,不知为何,这个案例将传递 store.isItemLoaded(parentItem) 检测逻辑,且您有一个 childItems=[true]。因为 ture 不是一个有效条目。树型网格将跳过这个无效的字条目,这就导致什么都没出现。查看清单 7 中 store.isItemLoaded 方法的代码,看看究竟发生了什么。


清单 7. store.isItemLoaded
				
isItemLoaded: function(/* anything */ something){
    // Currently we don't have any state that tells if an item is loaded or not
    // if the item exists it's also loaded.
    // This might change when we start working with refs inside items ...
    return this.isItem(something);
} 
           

此方法的注释提供了一个解释。QueryReadStore 这时不支持部分地 加载条目。


定制 QueryReadStore 以支持部分加载

在这一小节,您将定制 QueryReadStoreTreeGrid 来实现部分加载和修复回归 bug。

您可以 下载 定制的所有代码。这是一个易于配置的 web 项目,在 Eclipse 中运行。

创建 CustomQueryStore

要同时使用树型网格的展开功能和 QueryReadStore,您必须定制 QueryReadStore 使其能支持一个部分加载的条目。首先,您必须扩展这个类来添加缺少的方法,如清单 8 所示,只需将子类 QueryReadStore 添加到 CustomQueryStore


清单 8. 子类 QueryReadStore
				
/* treegrid-demo\WebContent\script\dojo-1.4.3\demo\data\CustomQueryStore.js */ 

dojo.provide("demo.data.CustomQueryStore"); 
dojo.require("dojox.data.QueryReadStore"); 

dojo.declare("demo.data.CustomQueryStore", dojox.data.QueryReadStore, { 
    /* @Override */ 
    isItemLoaded: function(/* anything */ something) { 
        // TODO 
    } 
}); 

下一步是修改规则检查一个条目是否已被加载,如清单 9 所示。


清单 9. isItemLoaded 方法
				
/* treegrid-demo\WebContent\script\dojo-1.4.3\demo\grid\CustomTreeGrid.js */ 

/* @Override isItemLoaded method */ 
isItemLoaded: function(/* anything */ something) { 
    // Currently we have item["children"] as a state that tells if an item is 
    // loaded or not. 
    // if item["children"] === true, means the item is not loaded. 
    var isLoaded = false; 

    if (this.isItem(something)) { 
        var children = this.getValue(something, "children"); 
        if (children === true) { 
            // need to lazy loading children 
            isLoaded = false; 
        } else { 
            isLoaded = true; 
        } 
    } 

    return isLoaded; 
} 
            

QueryReadStore 没有 loadItem 方法,因此,您要创建此方法。该方法将向服务器请求一个列出该条目所有子条目的数组。


清单 10. 重写 loadItem、getValues 和 setValues
				
/* treegrid-demo\WebContent\script\dojo-1.4.3\demo\grid\CustomTreeGrid.js */ 

/* @Override loadItem method */ 
loadItem: function(/* object */ args) { 
    if (this.isItemLoaded(args.item)) { 
        return; 
    } 

    var item = args.item; 
    var scope = args.scope || dojo.global; 
    var sort = args.sort || null; 
    var onItem = args.onItem; 
    var onError = args.onError; 

    if (dojo.isArray(item)) { 
        item = item[0]; 
    } 

    // load children 
    var children = this.getValue(item, "children"); 

    // load children 
    if (children === true) { 
        var serverQuery = {}; 

        // "parent" param 
        var itemId = this.getValue(item, "id"); 
        serverQuery["parent"] = itemId; 

        // "sort" param 
        if (sort) { 
            var attribute = sort.attribute; 
            var descending = sort.descending; 
            serverQuery["sort"] = (descending ? "-" : "") + attribute; 
        } 

        // ajax request 
        var _self = this; 

        var xhrData = { 
            url: this.url, 
            handleAs: "json", 
            content: serverQuery 
        }; 

        var xhrFunc = (this.requestMethod.toLowerCase() === "post") ? 
                       dojo.xhrPost : dojo.xhrGet; 
        var deferred = xhrFunc(xhrData); 

        // onError callback 
             deferred.addErrback(function(error) { 
            if (args.onError) { 
                args.onError.call(scope, error); 
            } 
        }); 

        // onLoad callback 
        deferred.addCallback(function(data) { 
            if (!data) { 
                return; 
            } 

            if (dojo.isArray(data)) { 
                var children = data; 

                var parentItemId = itemId; 
                var childItems = []; 

                dojo.forEach(children, function(childData) { 
                    // build child item 
                    var childItem = {}; 
                    childItem.i = childData; 
                    childItem.r = this; 

                    childItems.push(childItem); 
                }, _self); 

                _self.setValue(item, "children", childItems); 
            } 

            if (args.onItem) { 
                args.onItem.call(scope, item); 
            } 
        }); 
    } 
} 

/* @Override geValues method */ 
getValues: function(item, attribute) { 
    //  summary: 
    //      See dojo.data.api.Read.getValues() 

    this._assertIsItem(item); 
    if (this.hasAttribute(item, attribute)) { 
        return item.i[attribute] || []; 
    } 

    return []; // Array 
} 

/* @Override seValue method */ 
setValue: function(/* item */ item, /* attribute-name-string */ attribute, 
                   /* almost anything */ value) { 
    // summary: See dojo.data.api.Write.set() 

    // Check for valid arguments 
    this._assertIsItem(item); 
    this._assert(dojo.isString(attribute)); 
    this._assert(typeof value !== "undefined"); 

    var success = false; 
    var _item = item.i; 
    _item[attribute] = value; 
    success = true; 

    return success; // boolean 
} 
            

现在,示例中有了一个新 CustomQueryStore。在 JavaScript 中替换 QueryReadStore,如清单 11 所示。


清单 11. 使用 CustomQueryStore 发送一个请求
				
var url = "./servlet/QueryStoreServlet";
var store = new demo.data.CustomReadStore({
    url: url,
    requestMethod: "get"
});
            

代码返回图5 所示的结果。


图 5. customQueryStore 返回的结果
显示名称、年龄、工作职务、职称和电话号码、在经理的子条目显示员工

定制 TreeGrid 来修复回归 bug

是时候核查其他特性了,确保应用 CustomQueryStore 之后就没有回归 bug 了。

首先,展开一行,然后单击 Name 标题执行排序。如图 6 所示,出现一个错误。


图 6. CustomQueryStore 排序之后出现错误
第一行上出现‘sorry an error occurred’ 的屏幕截图

调查之后,您发现树型网格总是保留 expando 函数的状态。排序之前,第 4 行是展开的,树型网格记住了这个状态。排序之后,条目有所不同,但是属性网格仍然想要打开第 4 行的 expando 功能。错误就出现了,因为现在第 4 行的条目没有子条目。当树型网格执行一个排序查询时,您需要清除 expando 状态。

正如您定制 QueryReadStore 一样,您需要定制 TreeGrid。然后,使用 CustomTreeGrid 替换 TreeGrid 将可以修复排序缺陷。


清单 12. 定制 TreeGrid
				
dojo.provide("demo.grid.CustomTreeGrid");
dojo.require("dojox.grid.TreeGrid");
dojo.declare("demo.grid.CustomTreeGrid", dojox.grid.TreeGrid, {
    /* @Override */
    sort: function() {
        this.closeExpando();

        this.inherited(arguments);
    },

    closeExpando: function(identities) {
        if (identities) {
            if (dojo.isArray(identities)) {
                // close multiple expando
                dojo.forEach(identities, function(identity) {
                    this._closeExpando(identity);
                }, this);
            } else {
                // close single expando
                var identity = identities;
                this._closeExpando(identity);
            }
        } else {
            // close all expando
            var expandoCell = this.getCell(this.expandoCell);
            for (var identity in expandoCell.openStates) {
                this._closeExpando(identity);
            }
        }
    },

    _closeExpando: function(identity) {
        var expandoCell = this.getCell(this.expandoCell);

        if (expandoCell.openStates.hasOwnProperty(identity) === true) {
            var open = expandoCell.openStates[identity] || false;
            if (open === true) {
                // clean up expando cache
                this._cleanupExpandoCache(null, identity, null);
            }
        }
    }

});
            

合起来,TreeGridQueryReadStore 是一个功能强大的处理延迟加载数据的组合。不需要大量前期数据转换就可显示规模庞大的分层数据。您可以利用 QueryReadStore 的部分加载支持对每次展开用一个请求执行延迟加载


性能比较

在本文中,我们比较使用 ItemFileReadStore 和使用 QueryReadStoreTreeGrid 的性能。图 7 和表 1 显示了定制的 QueryStore,网格使用的仅是使用 ItemFileReadStore 和基本 TreeGrid 时的 1/30 时长。


图 7. ItemFileReadStore 和 CustomQueryStore 的性能比较
显示 itemfilereadstore 读取时间为 1400,而 queryreadstore 显示少于 100 的条形图。

表 1. 性能比较
使用的数据存储库 服务器加载时间(秒) 网格渲染时间(秒) 总计
ItemFileReadStore 1.45 12.65 14.1
CustomQueryStore 0.14 0.37 0.51

结束语

在本文中,您学习了如何创建一个定制的解决方案来提升 Dojo 加载大数据的性能。您也学习了 QueryReadStoreTreeModelTreeGrid 如何共同合作来获取和渲染大数据。定制 QueryStoreTreeGrid 可以解决目前 Dojo 不能很好地支持大数据这一问题。本文结果显示解决方案的性能大约是 ItemFileReadStoreTreeGrid 所用时间的 1/30。



下载

描述名字大小下载方法
本文样例代码treegrid-demo.zip31KBHTTP

关于下载方法的信息


参考资料

学习

获得产品和技术

讨论

作者简介

//www.ibm.com/developerworks/i/p-shaixu.jpg

Sheng Hai Xu 是上海 IBM China Development Lab 的 Globalization Development 小组的一名软件工程师。他在使用 Dojo 和 Web 2.0 技术开发基于 web 的工具方面有着丰富的经验。

http://www.ibm.com/developerworks/i/p-yliu.jpg

Yang Liu 是位于中国上海的 IBM China Software Development Lab 的一名软件工程师,她在上海 Globalization Lab 工作,致力于 RFID 技术、 Web 服务和全球化技术的研究。

http://www.ibm.com/developerworks/i/p-jhu.jpg

Jie Hu 是上海 IBM China Development Lab 的 Globalization Development 小组的技术主管。他有 6 年的 J2EE 开发经验。

关于报告滥用的帮助

报告滥用

谢谢! 此内容已经标识给管理员注意。


关于报告滥用的帮助

报告滥用

报告滥用提交失败。 请稍后重试。


developerWorks:登录


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 使用条款

 


当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

请选择您的昵称:

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

(长度在 3 至 31 个字符之间)


单击提交则表示您同意developerWorks 的条款和条件。 使用条款.

 


为本文评分

评论

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Web development
ArticleID=776946
ArticleTitle=提升 Dojo TreeGrid 性能
publish-date=11282011

标签

Help
使用 搜索 文本框在 My developerWorks 中查找包含该标签的所有内容。

使用 滑动条 调节标签的数量。

热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。

我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。

使用搜索文本框在 My developerWorks 中查找包含该标签的所有内容。热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。