vue 使用 vxe-list 实现虚拟列表实现虚拟树,virtual list 大数据量树列表

vxe-table vue 使用 vxe-list 实现虚拟列表实现虚拟树,virtual list 大数据量树列表

在 vue 渲染树结构是比较卡的,特别是数据大了,展开缩写都要几秒,那么解决问题的思路就是减少 dom 渲染,使用虚拟列表来模拟树结构,这个方式可以渲染大量的数据,缺点是每次点击展开、收缩都要重新更新列表,效果略差,但是支持渲染大数据量。

实现虚拟树的步骤:

  1. 基于 vxe-list 虚拟列表
  2. 拍平树结构
  3. 构建列表树结构
  4. 处理展开收缩

测试中:渲染 5w 条数据平均在 200ms 以内
理论上流畅渲染的数量在10w 条左右,因为数据量太大的话 js 运算树节点都会很耗时

 <vxe-list height="400" class="my-tree" :loading="loading" :data="list">
   <template v-slot="{ items }">
     <div
       class="my-tree-item"
       v-for="item in items"
       :key="item.id"
       :class="[`level-${item._LEVEL}`, {'has-child': item._HAS_CHILDREN, 'is-expand': item._EXPAND}]"
       :style="{paddingLeft: `${item._LEVEL * 20}px`}">
       <i class="tree-icon fa fa-chevron-right" @click="toggleTreeNode(item)">i>
       <span class="tree-label">{{ item.label }}span>
     div>
   template>
 vxe-list>
export default {
	data () {
		return {
			loading: false,
    	    list: []
		}
	},
	created () {
		this.loadTree(10)
	},
	methods: {
		getTree (size) {
	        // 模拟后台数据
	        const result = []
	        let idKey = 0
	        for (let index = 0; index < size; index++) {
	        const item = {
	            id: ++idKey,
	            label: `节点 ${index}`
	        }
	        if (index) {
	            if (index % 33 === 0) {
	            const childList = []
	            for (let cIndex = 0; cIndex < 1000; cIndex++) {
	                childList.push({
	                id: ++idKey,
	                label: `子节点 ${index}-${cIndex}`,
	                children: [
	                    { label: `子节点 ${index}-${cIndex}-0` },
	                    { label: `子节点 ${index}-${cIndex}-1` },
	                    { label: `子节点 ${index}-${cIndex}-2` },
	                    { label: `子节点 ${index}-${cIndex}-3` },
	                    { label: `子节点 ${index}-${cIndex}-4` },
	                    { label: `子节点 ${index}-${cIndex}-5` },
	                    { label: `子节点 ${index}-${cIndex}-6` },
	                    { label: `子节点 ${index}-${cIndex}-7` }
	                ]
	                })
	            }
	            item.children = childList
	            } else if (index % 22 === 0) {
	            const childList = []
	            for (let cIndex = 0; cIndex < 500; cIndex++) {
	                childList.push({
	                id: ++idKey,
	                label: `子节点 ${index}-${cIndex}`,
	                children: [
	                    { label: `子节点 ${index}-${cIndex}-0` },
	                    { label: `子节点 ${index}-${cIndex}-1` },
	                    { label: `子节点 ${index}-${cIndex}-2` }
	                ]
	                })
	            }
	            item.children = childList
	            } else if (index % 9 === 0) {
	            const childList = []
	            for (let cIndex = 0; cIndex < 200; cIndex++) {
	                childList.push({
	                id: ++idKey,
	                label: `子节点 ${index}-${cIndex}`,
	                children: [
	                    { label: `子节点 ${index}-${cIndex}-0` },
	                    { label: `子节点 ${index}-${cIndex}-1` },
	                    { label: `子节点 ${index}-${cIndex}-2` },
	                    { label: `子节点 ${index}-${cIndex}-4` },
	                    { label: `子节点 ${index}-${cIndex}-5` }
	                ]
	                })
	            }
	            item.children = childList
	            } else if (index % 6 === 0) {
	            const childList = []
	            for (let cIndex = 0; cIndex < 100; cIndex++) {
	                childList.push({
	                id: ++idKey,
	                label: `子节点 ${index}-${cIndex}`,
	                children: [
	                    { label: `子节点 ${index}-${cIndex}-0` },
	                    { label: `子节点 ${index}-${cIndex}-1` },
	                    { label: `子节点 ${index}-${cIndex}-2` },
	                    { label: `子节点 ${index}-${cIndex}-3` }
	                ]
	                })
	            }
	            item.children = childList
	            } else if (index % 3 === 0) {
	            const childList = []
	            for (let cIndex = 0; cIndex < 10; cIndex++) {
	                childList.push({
	                id: ++idKey,
	                label: `子节点 ${index}-${cIndex}`,
	                children: [
	                    { label: `子节点 ${index}-${cIndex}-0` },
	                    { label: `子节点 ${index}-${cIndex}-1` }
	                ]
	                })
	            }
	            item.children = childList
	            }
	        }
	        result.push(item)
	        }
	        return result
	    },
	    loadTree (size) {
	        this.loading = true
	        setTimeout(() => {
	        const trerData = this.getTree(size)
	        // 将树结构拍平,构建列表树结构
	        XEUtils.eachTree(trerData, (item, index, items, paths, parent, nodes) => {
	            // 层级
	            item._LEVEL = nodes.length - 1
	            // 是否展开
	            item._EXPAND = false
	            // 是否可视
	            item._VISIBLE = !item._LEVEL
	            // 是否有子节点
	            item._HAS_CHILDREN = item.children && item.children.length > 0
	            // 是否叶子节点
	            item._IS_LEAF = !item._HAS_CHILDREN
	        })
	        this.tree = trerData
	        this.refreshTree()
	        this.loading = false
	        const startTime = Date.now()
	        this.$nextTick(() => {
	            this.$XModal.message({ message: `渲染 ${this.fullList.length} 行,用时 ${Date.now() - startTime}毫秒`, status: 'info' })
	        })
	        }, 200)
	    },
	    // 切换树节点的展开、收缩
	    toggleTreeNode (row) {
	        if (row._HAS_CHILDREN) {
	        this.setTreeExpand(row, !row._EXPAND)
	        }
	    },
	    // 设置树节点的展开、收缩
	    setTreeExpand (row, isExpand) {
	        const matchObj = XEUtils.findTree(this.tree, item => item === row)
	        row._EXPAND = isExpand
	        if (matchObj) {
	        XEUtils.eachTree(matchObj.item.children, (item, index, items, path, parent) => {
	            item._VISIBLE = parent ? parent._EXPAND && parent._VISIBLE : isExpand
	        })
	        }
	        this.refreshTree()
	    },
	    // 展开、收缩所有树节点
	    allTreeExpand (isExpand) {
	        if (isExpand) {
	        XEUtils.eachTree(this.tree, item => {
	            item._EXPAND = item._HAS_CHILDREN
	            item._VISIBLE = true
	        })
	        } else {
	        XEUtils.eachTree(this.tree, item => {
	            item._EXPAND = false
	            item._VISIBLE = !item._LEVEL
	        })
	        }
	        this.refreshTree()
	    },
	    refreshTree () {
	        const treeList = XEUtils.toTreeArray(this.tree)
	        this.fullList = treeList
	        this.list = treeList.filter(item => item._VISIBLE)
	    }
	}
}

在线运行 http://jsrun.net/3Z2Kp/edit

你可能感兴趣的:(vue)