在我们的项目中,有一个目录树,这个目录树组件用的是element-ui
中的el-tree
。但是当有上万个节点时,渲染会有点卡。由于时间紧,任务大,我想了一个临时方案(为什么说是临时方案呢?因为针对这种场景可能会有更好的方案):树结构第一级不做懒加载,直接请求,第二级懒加载,因为第一级数据不多,第二级数据多,在第二级懒加载时分页请求,即第一次请求50条,点击更多,请求第二页,更多按钮总跟在第二级节点最后。
实现
<template>
<div>
<el-input v-model="query" @input="handleInput"></el-input>
<el-tree
ref="myTree"
:data="treeData"
v-loading="loading"
node-key="id"
:indent="10"
@node-click="handleNodeClick"
:highlight-current="true"
:expand-on-click-node="true"
lazy
:load="loadNode"
>
<div class="custom-tree-node" slot-scope="{ node, data }">
<div v-if="node.label==='加载更多'" class="left-tree__more" @click.stop="handleMore(data)">
{{ node.label }}
</div>
<span v-else>
{{ node.label }}
</span>
</div>
</el-tree>
</div>
</template>
<script>
class ReqObj {
constructor (options) {
for (const name in options) {
if (Object.prototype.hasOwnProperty.call(options, name)) {
this[name] = options[name]
}
}
this.pageSize = 50
this.pageNum = 1
this.total = 0
this.list = []
this.start()
}
async reqMethod () {
// 请求数据
const res = await getData({
pageNum: this.pageNum,
pageSize: this.pageSize
})
const data = res.data || {}
this.total = data.total || 0
const list = data.list || []
list.forEach(item => {
item.isLeaf = true
})
if (this.total === 0) {
list.push({ id: Symbol(''), label: '暂无数据', isLeaf: true })
}
this.list = this.list.concat(list)
let result = []
if (this.pageNum * this.pageSize < this.total) {
// 如果还有数据,最后一项是“加载更多”
result = this.list.concat([{
id: Symbol(this.node.id),
label: '加载更多',
isLeaf: true,
$nodePid: this.node.id // 携带父节点的id,在reqManages中通过这个id查找ReqObj实例
}])
} else {
result = this.list
}
this.resolve(result)
return result
}
loadMore () {
this.pageNum += 1
return this.reqMethod()
}
start () {
this.reqMethod()
}
}
export default {
name: '',
data () {
return {
loading: false,
query: '',
treeData: [],
defaultProps: {
children: 'children',
label: 'label',
isLeaf: 'isLeaf'
},
reqManages: {}
}
},
created () {
},
methods: {
handleInput () {
this.handleSearch()
},
// 点击加载更多
async handleMore (item) {
if (this.reqManages[item.$nodePid]) {
this.loading = true
await this.reqManages[item.$nodePid].loadMore()
this.loading = false
}
},
async loadNode (node, resolve) {
if (node.level === 1) {
if (this.reqManages[node.id]) return
// new一个ReqObj实例保存到reqManages中,key是node.id
this.reqManages[node.id] = new ReqObj({
resolve, // 传入当前这个resolve
node, // 传入点开的这个node
vm: this // 当前vue实例
})
}
},
clearReqManages () {
this.reqManages = {}
},
handleSearch () {
this.treeData = []
this.clearReqManages()
// 模拟请求
// 目录树第一级不用懒加载,展开时再触发懒加载
setTimeout(() => {
this.expandFirst()
}, 1000)
},
// 展开第一个节点
expandFirst () {
this.$nextTick(() => {
const firstItem = this.treeData[0]
const node = this.$refs.myTree.getNode(firstItem.id)
// 调用expand方法触发懒加载
node.expand()
})
}
}
}
</script>
<style lang="scss" scoped>
.left-tree__more {
width: 100%;
text-align: center;
cursor: pointer;
color: $--color-primary
}
</style>