在 Ext JS 的树组件中,无论是叶子节点还是非叶子节点,均采用统一的 TreeNode 类来表示——这一设计保持了高度统一。然而,根据节点类型,具体分为两种:一种为常规简单树节点,由 Ext.tree.TreeNode 定义;另一种为支持异步加载子节点的节点,由 Ext.tree.AsyncTreeNode 定义。简而言之,对于数据量较小且结构固定的场景,推荐使用前者;若需要按需从服务端动态获取数据,则应选择后者。
在数据层面,核心字段主要包括:text 用于显示节点文字,leaf 标记是否为叶子节点,children 存放子节点数组,expanded 控制初始展开状态。一个典型的数据结构示例如下:
var store = Ext.create('Ext.data.TreeStore', {
root: {
expanded: true,
children: [
{ text: "留学", leaf: true },
{ text: "功课", expanded: true, children: [
{ text: "英语", leaf: true },
{ text: "代数", leaf: true }
] },
{ text: "托福", leaf: true }
]
}
});
TreePanel 从 Servlet 中读取 JSON 数据
在实际项目中,树的数据很少硬编码在页面内,通常需要从服务器动态加载。本示例通过一个返回 JSON 数据的 Servlet 进行演示,前端 TreePanel 借助 loader 与该 Servlet 进行通信。
首先来看服务端代码——一个简单的 TreeNodeServlet,根据请求参数 node 返回不同的 JSON 片段:
import ja va.io.IOException;
import ja va.io.PrintWriter;
import ja vax.servlet.http.HttpServletRequest;
import ja vax.servlet.http.HttpServletResponse;
import ja vax.servlet.ServletException;
import ja vax.servlet.http.HttpServlet;
public class TreeNodeServlet extends HttpServlet {
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
String treeNode = request.getParameter("node");
String json = "";
PrintWriter out = response.getWriter();
if ("0".equals(treeNode)) {
json += "[{id:1, text:'0-1'}, {id:2, text:'0-2'}]";
} else if ("1".equals(treeNode)) {
json += "[{id:11, text:'0-1-1', leaf:true}, {id:12, text:'0-1-2', leaf:true}]";
} else if ("2".equals(treeNode)) {
json += "[{id:21, text:'0-2-1'}, {id:22, text:'0-2-2', leaf:true}]";
} else if ("21".equals(treeNode)) {
json += "[{id:211, text:'0-2-1-1', leaf:true}, {id:212, text:'0-2-1-2', leaf:true}]";
}
out.write(json);
}
}
前端代码则简洁明了:创建一个 TreePanel,将 loader 的 dataUrl 指向上述 Servlet,然后设置根节点(注意此处需要使用 AsyncTreeNode,因为数据采用懒加载方式),最后调用 root.expand() 触发首次加载。
Ext.onReady(function() {
var tree = new Ext.tree.TreePanel({
el: 'div-tree',
loader: new Ext.tree.TreeLoader({ dataUrl: '../TreeNodeServlet' })
});
var root = new Ext.tree.AsyncTreeNode({ id: '0', text: '0' });
tree.setRootNode(root);
tree.render();
root.expand();
});
效果截图如下:

在树 TreePanel 之间拖放节点
在某些场景下,我们需要将一个树的节点拖拽到另一个树中。同一棵树内的拖动,只需设置 enableDD: true 即可;跨树拖拽则需要分别配置 enableDrag 和 enableDrop。以下是一个完整的实现示例。
1. JS 代码
Ext.onReady(function() {
var tree1 = new Ext.tree.TreePanel({
el: 'tree1',
enableDrag: true, // 允许从此树拖出
loader: new Ext.tree.TreeLoader({ dataUrl: 'treeData1.txt' })
});
var tree2 = new Ext.tree.TreePanel({
el: 'tree2',
enableDrop: true, // 允许将元素拖入此树
loader: new Ext.tree.TreeLoader({ dataUrl: 'treeData2.txt' })
});
var root1 = new Ext.tree.AsyncTreeNode({ text: '根节点' });
var root2 = new Ext.tree.AsyncTreeNode({ text: '图书' });
tree1.setRootNode(root1);
tree2.setRootNode(root2);
tree1.render();
tree2.render();
});
2. HTML 代码
3. 数据文件(JSON 格式)
treeData1.txt:
[
{ text: '非叶子结点' },
{ text: '叶子结点', leaf: true }
]
treeData2.txt:
[
{ text: '计算机', children: [
{ text: 'Ja va', children: [
{ text: 'Ja va核心技术', leaf: true },
{ text: 'Thinking in Ja va', leaf: true }
]},
{ text: '算法导论', leaf: true }
]},
{ text: '音乐', children: [
{ text: '乐理基础', leaf: true },
{ text: '卡尔卡西古典吉他教程', leaf: true }
]}
]
4. 运行效果
下图展示了两个树之间的拖放交互效果:

以上即为 Ext JS 树组件在数据加载与跨树拖放方面的典型用法。在实际项目中,可根据需求扩展节点数据、添加事件监听,甚至实现自定义拖放逻辑。需要特别留意:AsyncTreeNode 是异步加载的核心,而 enableDrag 与 enableDrop 则是跨树拖放的关键配置项。
