virtual-tree-component
v2.0.0
Published
A virtual tree component for Vue.js
Readme
Vue Virtual Tree Component
基于 Element UI Tree 组件改造的虚拟滚动树组件,支持大数据量高效渲染。
功能特性
核心功能
- ✅ 虚拟滚动:支持大数据量(10万+节点)高效渲染
- ✅ 兼容 Element UI Tree:支持 Element UI Tree 的大部分 API
- ✅ 可配置行高:支持自定义节点高度
- ✅ 自适应模式:可切换为非虚拟滚动模式
- ✅ 多种展开方式:支持默认展开、手动展开等
交互功能
- ✅ 节点选择:支持单选、多选(checkbox)
- ✅ 节点拖拽:支持节点拖拽排序和移动
- ✅ 节点展开/折叠:支持点击展开、双击展开等
- ✅ 节点过滤:支持根据关键词过滤节点
- ✅ 高亮当前节点:支持高亮显示当前选中节点
性能优化
- ✅ 按需渲染:只渲染可见区域内的节点
- ✅ 回收复用:节点DOM元素会被回收复用
- ✅ 缓冲机制:预渲染可见区域外的节点,提升滚动流畅度
安装
使用 npm 安装
npm install virtual-tree-component --save使用 yarn 安装
yarn add virtual-tree-component依赖
- Vue.js:^2.6.14
- vue-virtual-scroller:^1.1.2
- Element UI:可选,组件样式基于 Element UI Tree
快速开始
全局引入
import Vue from 'vue'
import VirtualTree from 'virtual-tree-component'
import 'virtual-tree-component/assets/index.scss'
Vue.component('virtual-tree', VirtualTree)局部引入
<template>
<div class="app">
<virtual-tree
:data="treeData"
:height="400"
:item-size="26"
@node-click="handleNodeClick"
/>
</div>
</template>
<script>
import VirtualTree from 'virtual-tree-component'
import 'virtual-tree-component/assets/index.scss'
export default {
components: {
VirtualTree
},
data() {
return {
treeData: [
{
label: '一级 1',
children: [
{
label: '二级 1-1',
children: [
{
label: '三级 1-1-1'
}
]
}
]
},
{
label: '一级 2',
children: [
{
label: '二级 2-1',
children: [
{
label: '三级 2-1-1'
}
]
},
{
label: '二级 2-2',
children: [
{
label: '三级 2-2-1'
}
]
}
]
}
]
}
},
methods: {
handleNodeClick(data, node) {
console.log('点击了节点:', data)
}
}
}
</script>
<style scoped>
.app {
width: 400px;
margin: 20px auto;
}
</style>API
Props
| 参数 | 类型 | 默认值 | 说明 | |------|------|--------|------| | 基础配置 | | | | | data | Array | [] | 树的数据源 | | height | [String, Number] | 0 | 树的高度,设置后启用虚拟滚动 | | item-size | Number | 26 | 每个节点的高度(单位:px) | | props | Object | { children: 'children', label: 'label', disabled: 'disabled' } | 配置树节点的属性 | | node-key | String | - | 每个树节点用来作为唯一标识的属性 | | 显示配置 | | | | | show-checkbox | Boolean | false | 是否显示复选框 | | highlight-current | Boolean | false | 是否高亮当前选中节点 | | indent | Number | 18 | 相邻级节点间的水平缩进,单位为像素 | | icon-class | String | - | 自定义树节点的图标 | | render-content | Function | - | 自定义节点内容 | | 交互配置 | | | | | expand-on-click-node | Boolean | true | 是否在点击节点的时候展开或者收缩节点 | | check-on-click-node | Boolean | false | 是否在点击节点的时候选中节点 | | auto-expand-parent | Boolean | true | 是否自动展开父节点 | | default-expand-all | Boolean | false | 是否默认展开所有节点 | | default-expanded-keys | Array | [] | 默认展开的节点的 key 的数组 | | default-checked-keys | Array | [] | 默认选中的节点的 key 的数组 | | current-node-key | [String, Number] | - | 当前选中的节点 | | check-strictly | Boolean | false | 在显示复选框的情况下,是否严格的遵循父子不互相关联的做法 | | check-descendants | Boolean | false | 当 check-strictly 为 false 时,是否自动检查子节点 | | draggable | Boolean | false | 是否开启拖拽功能 | | allow-drag | Function | - | 拖拽时判定节点是否可拖拽的函数 | | allow-drop | Function | - | 拖拽时判定目标节点能否被放置的函数 | | 性能配置 | | | | | render-after-expand | Boolean | true | 是否在第一次展开某个树节点后才渲染其子节点 | | lazy | Boolean | false | 是否懒加载子节点,需与 load 方法结合使用 | | load | Function | - | 加载子树数据的方法,仅当 lazy 属性为 true 时生效 | | keeps | Number | 40 | 虚拟滚动中保持的节点数量 | | extra-line | Number | 8 | 虚拟滚动中额外渲染的行数 | | 其他配置 | | | | | accordion | Boolean | false | 是否每次只打开一个同级树节点 | | filter-node-method | Function | - | 对树节点进行筛选时执行的方法 | | empty-text | String | '暂无数据' | 内容为空时的提示文本 |
Events
| 事件名称 | 回调参数 | 说明 | |----------|----------|------| | node-click | data, node, component | 节点被点击时触发 | | node-contextmenu | event, data, node, component | 节点被鼠标右键点击时触发 | | node-dblclick | data, node, component | 节点被双击时触发 | | node-expand | data, node, component | 节点被展开时触发 | | node-collapse | data, node, component | 节点被折叠时触发 | | node-check | data, checked, node | 节点复选框被点击时触发 | | check-change | data, checked, indeterminate | 节点选中状态发生变化时触发 | | current-change | data, node | 当前选中节点变化时触发 | | node-drag-start | node, event | 节点开始拖拽时触发 | | node-drag-enter | draggingNode, dropNode, dropType | 拖拽进入其他节点时触发 | | node-drag-leave | draggingNode, dropNode | 拖拽离开节点时触发 | | node-drag-over | draggingNode, dropNode, event | 拖拽经过节点时触发 | | node-drag-end | draggingNode, dropNode, dropType, event | 拖拽结束时触发 | | node-drop | draggingNode, dropNode, dropType, event | 拖拽成功完成时触发 |
Methods
| 方法名 | 参数 | 说明 | |--------|------|------| | filter | value | 对树节点进行筛选操作 | | updateKeyChildren | key, data | 通过 key 更新节点的子节点 | | append | data, parentNode | 为某个节点追加子节点 | | remove | data | 删除某个节点 | | insertBefore | data, refNode | 在参考节点前插入节点 | | insertAfter | data, refNode | 在参考节点后插入节点 | | getCheckedNodes | leafOnly, includeHalfChecked | 如果节点可被选择(show-checkbox 为 true),则返回当前选中的节点 | | getCheckedKeys | leafOnly | 如果节点可被选择(show-checkbox 为 true),则返回当前选中节点的 key 组成的数组 | | setCheckedNodes | nodes | 设置目前勾选的节点,使用此方法必须设置 node-key 属性 | | setCheckedKeys | keys, leafOnly | 通过 keys 设置目前勾选的节点,使用此方法必须设置 node-key 属性 | | setChecked | data, checked, deep | 设置节点是否选中,使用此方法必须设置 node-key 属性 | | getCurrentNode | - | 获取当前被选中的节点 | | setCurrentNode | node | 设置当前选中的节点,使用此方法必须设置 node-key 属性 | | getNode | data | 根据 data 或者 key 拿到对应的 node |
高级用法
虚拟滚动模式
通过设置 height 属性启用虚拟滚动:
<virtual-tree
:data="treeData"
:height="400px"
:item-size="26"
:default-expand-all="true"
/>自定义节点内容
使用 render-content 属性自定义节点内容:
<virtual-tree
:data="treeData"
:height="400"
:render-content="renderContent"
/>
<script>
export default {
methods: {
renderContent(h, { node, data, store }) {
return (
<div>
<span>{data.label}</span>
<button size="mini" onClick={() => this.handleEdit(data)}>
编辑
</button>
</div>
)
},
handleEdit(data) {
console.log('编辑节点:', data)
}
}
}
</script>拖拽功能
启用拖拽功能:
<virtual-tree
:data="treeData"
:height="400"
:draggable="true"
@node-drop="handleNodeDrop"
/>
<script>
export default {
methods: {
handleNodeDrop(draggingNode, dropNode, dropType) {
console.log('拖拽完成:', draggingNode.data, dropNode.data, dropType)
}
}
}
</script>懒加载
启用懒加载功能:
<virtual-tree
:data="treeData"
:height="400"
:lazy="true"
:load="loadNode"
node-key="id"
/>
<script>
export default {
methods: {
loadNode(node, resolve) {
if (node.level === 0) {
return resolve([{ name: '一级节点', id: 1 }])
}
if (node.level > 2) {
return resolve([])
}
// 模拟异步加载
setTimeout(() => {
const data = []
for (let i = 1; i <= 10; i++) {
data.push({
name: `${node.data.name}-${i}`,
id: `${node.data.id}-${i}`
})
}
resolve(data)
}, 500)
}
}
}
</script>版本依赖要求
| Vue Version | Virtual Tree Version | vue-virtual-scroller Version | |-------------|----------------------|-------------------------------| | Vue 2.6.x | ^1.0.0 | ^1.1.2 | | Vue 2.7.x | ^1.0.0 | ^1.1.2 |
注意事项
虚拟滚动模式下:
- 必须设置
height属性才能启用虚拟滚动 - 建议设置固定的
item-size,确保滚动位置准确 - 节点内容应保持固定高度,避免高度变化影响滚动
- 必须设置
性能优化:
- 对于大数据量,建议使用
node-key属性提高节点查找效率 - 复杂节点内容建议使用
render-content而非插槽 - 启用
render-after-expand可以减少初始渲染时间
- 对于大数据量,建议使用
兼容性:
- 兼容 Element UI Tree 的大部分 API,但某些高级功能可能有所不同
- 支持现代浏览器,IE11 可能需要额外的 polyfill
样式问题:
- 组件样式基于 Element UI Tree,建议引入 Element UI 样式
- 或根据需要自定义样式
许可证
MIT
贡献
欢迎提交 Issue 和 Pull Request!
更新日志
v1.0.0
- 初始版本发布
- 支持虚拟滚动
- 兼容 Element UI Tree API
- 支持拖拽、多选、懒加载等功能
常见问题
Q: 为什么设置了 height 属性但虚拟滚动不生效?
A: 请检查以下几点:
- 确保
height属性值大于 0 - 确保数据已正确加载
- 检查是否有样式冲突导致高度计算错误
Q: 为什么拖拽功能不工作?
A: 请检查:
- 是否已设置
draggable为true - 确保已设置
node-key属性 - 检查
allow-drag和allow-drop函数是否返回正确值
Q: 为什么节点内容显示异常?
A: 请检查:
render-content函数是否正确返回 VNode- 节点数据的
label属性是否存在 - 样式是否有冲突
项目地址
- Gitee: Gitee address
感谢使用 Vue Virtual Tree Component!
