卡顿假死
服务端返回全部数据时,有两三千条,渲染全量的tree组件时前端直接卡死,排查发现业务数据大部分子节点的量级在万以上。页面卡死,改成懒加载模式后.树形选择器使用了vue-easy-tree
npm install @wchbrad/vue-easy-tree
vue-easy-tree这个插件的具体使用看这个文章
组件中使用
import VueEasyTree from "@wchbrad/vue-easy-tree";
// 样式文件,可以根据需要自定义样式或主题
import "@wchbrad/vue-easy-tree/src/assets/index.scss"
export default {
components: {
VueEasyTree
}
}
此处只演示了几条数据,vue-easy-tree作为虚拟滚动时,必须给个固定高度和node-key
<template>
<div>
<el-select
v-model="value"
multiple
v-bind="$attrs"
collapse-tags
style="width:100%"
popper-class="treeDown"
@remove-tag="removeTag"
>
<el-option value="" />
<vue-easy-tree
ref="treeOption"
:node-key="nodeKey"
height="280px"
:data="options"
show-checkbox
:props="defaultProps"
:default-checked-keys="defaultCheckNodes"
:check-strictly="true"
@check="checkNode"
@check-change="checkChange"
></vue-easy-tree>
</el-select>
</div>
</template>
<script>
export default {
name: 'DepSelector',
props: {
// 可用选项的数组
options: {
type: Array,
default: () => []
},
// 配置选项
defaultProps: {
type: Object,
default: () => ({
// 属性值为后端返回的对应的字段名
children: 'children',
label: 'name',
})
},
nodeKey: {
type: String,
default: () => {
return 'id'
},
},
// 默认勾选的节点
defaultCheckNodes: {
type: Array, // 已经分配的资源
default: () => {
return []
},
},
},
data() {
return {
// 文本框中的标签
value: [],
// 输入框回显值
treeLabel: [],
// 勾选数据的完整节点
allTreeList: [],
}
},
watch:{
defaultCheckNodes:{
handler(val) {
//监听父组件传给子组件默认绑定的值
this.$nextTick(()=>{
//获取当前选中的节点
this.allTreeList = this.$refs.treeOption.getCheckedNodes()
//获取选中的keys数组
this.treeLabel = this.$refs.treeOption.getCheckedKeys()
//获取选中值的name,赋给select选择框
this.value = this.allTreeList.map(item => {
return item.name
})
this.$emit('input', this.value)
})
},
deep:true,
immediate: true
}
},
methods: {
reset() {
this.$refs.treeOption.setCheckedKeys([])
},
// 删除tag时,
removeTag(val) {
// 取消勾选
// console.log(this.nodeKey)
//移除的tag永远是选中节点的第一个
const treeOption = this.$refs.treeOption
treeOption.setChecked(this.treeLabel[0], false, false)
},
// 勾选节点触发事件
checkNode(currentObj, treeStatus) {
// 重新给控件赋值
this.hanleCheck(currentObj, treeStatus, 'treeOption')
},
hanleCheck(data, node, treeName) {
const _this = this
// 获取当前节点是否被选中
const children = _this.defaultProps.children
const isChecked = _this.$refs[treeName].getNode(data).checked
// 如果当前节点被选中,则遍历下级子节点并选中,如果当前节点取消选中,则遍历下级节点并取消
// 判断该节点是否有下级节点,如果有那么遍历设置下级节点为选中
if(Array.isArray(data[children]) && data[children].length) {
setChildreChecked(data[children],isChecked)
// let list = setChildreChecked(data[children],true)
// _this.$refs[treeName].setCheckedKeys([...new Set([...node.checkedKeys,...list])])
}
_this.$nextTick(()=>{
//组件赋值更新结束才能获取到选中的节点
this.allTreeList = this.$refs.treeOption.getCheckedNodes()
this.value = this.allTreeList.map(item => {
return item.name
})
this.treeLabel = this.$refs.treeOption.getCheckedKeys()
this.$emit('input', this.value)
})
function setChildreChecked(node,isChecked) {
// return node.reduce((arr,item)=>{
// if(Array.isArray(item[children])&&item[children].length) {
// arr.psuh(...setChildreChecked(item[children]))
// }
// arr.push(item[_this.nodeKey])
// return arr
// },[])
node.forEach((item) => {
item[children] &&
item[children].length > 0 &&
setChildreChecked(item[children], isChecked)
// 修改勾选状态
_this.$refs[treeName].setChecked(item[_this.nodeKey], isChecked)
})
}
},
//数据变更时触发
checkChange(data, checked, indeterminate) {
// console.log(data, checked,indeterminate,"checkChange");
// this.allTreeList = this.$refs.treeOption.getCheckedNodes()
// this.value = this.allTreeList.map(item => {
// return item.name
// })
// // 输入框回显
// this.treeLabel = this.$refs.treeOption.getCheckedKeys()
// console.log(this.allTreeList,this.treeLabel );
// // 重新给控件赋值
// this.$emit('input', this.value)
},
},
}
</script>
下面树形组件根据前面树形组件勾选的,下组件禁止勾选前面组件未勾选的数据.
效果
前面组件选中
下面组件勾选
watch: {
//orgTypeCodes 前面组件勾选的数据
'orgTypeCodes': {
handler(newVal, oldVal) {
//this.$refs.selectTree 组件的ref实例
if (this.$refs.selectTree && newVal) {
//this.allTreeList = this.$refs.treeOption.getCheckedNodes()勾选的节点
const allTreeList = cloneDeep(this.$refs.selectTree.allTreeList)
//克隆一份初始化树形数据options
const list = cloneDeep(this.treeMeta)
this.fn(list, allTreeList)
//下面组件的options等于这个返回的数据
this.optionsMetaAll = list
}
},
deep: true,
immediate: true
}
},
methods: {
fn(val, list = []) {
val.forEach(item => {
//将所有数据disabled置为true
item.disabled = true
//然后找出选中节点中是否有当前item
const e = list.find(it => it.code === item.code)
//如果有就把当前数据disabled置为false
e && (item.disabled = false)
//再判断是否有子节点 有则循环递归
if (Array.isArray(item.childrenOrgs) && item.childrenOrgs.length) {
this.fn(item.childrenOrgs, list)
}
})
}
}