使用 Element Plus UI 框架中的 el-tree 组件的示例。el-tree 是一个树形控件,通常用于展示具有层级关系的数据。下面是对代码中关键部分的介绍:
:树形控件的根元素,用于定义树的基本属性和事件。
class=“video-tree”:为树控件添加了一个 CSS 类,可能用于定制样式。
ref=“eltree”:为树控件注册了一个引用名称 eltree,这样可以通过 this.$refs.eltree 在 Vue 实例中访问树的实例方法和数据。
:data=“treeData”:绑定了树的数据源 treeData,这是一个包含树节点的数组。
:props=“{ children: ‘children’, label: ‘label’, hasChildren: ‘hasChildren’ }”:定义了树节点对象的属性配置,指定了子节点、标签和是否有子节点的字段名。
:lazy=“true”:启用了懒加载功能,意味着树节点的子节点将按需加载而不是一次性加载所有节点。
:load=“loadNode”:定义了懒加载时用于加载子节点的函数 loadNode。
@node-click=“handleNodeClick”:为树节点点击事件绑定了处理函数 handleNodeClick。
@node-contextmenu=“handleNodeMenuClick”:为树节点右键菜单事件绑定了处理函数 handleNodeMenuClick。
:accordion=“false”:设置手风琴效果为 false,意味着不会在点击节点时自动折叠其他同级节点。
:highlight-current=“true”:高亮当前选中的节点。
node-key=“id”:指定了树节点的唯一标识字段为 id。
<template #default=“{ node, data }”>:使用作用域插槽自定义树节点的内容,其中 { node, data } 是插槽的参数,包含了当前节点的信息。
:在每个树节点上使用 el-dropdown 组件来创建一个下拉菜单,触发方式为右键点击(trigger=“contextmenu”)。
:ref="‘dropdown’+ node.data.id ":为每个下拉菜单实例注册引用,引用名称与节点的 id 相关联。
@visible-change=“(visible) => handleVisibleChange(node, visible)”:为下拉菜单的可见性变化事件绑定处理函数 handleVisibleChange。
<template #dropdown>:使用作用域插槽自定义下拉菜单的内容,条件是当前节点等于 currentDrop。
和 :定义下拉菜单的菜单项,包括“新建”、“删除”、“重命名”和“属性”等操作。
@click=“handleModal”、@click=“deleteNodes”、@click=“renameNode” 和 @click=“handleMessageModal”:分别为下拉菜单项绑定了点击事件处理函数。
这段代码展示了如何在 Element Plus 的 el-tree 组件中集成懒加载、自定义节点内容、下拉菜单以及事件处理
<el-tree
class="video-tree"
ref="eltree"
:data="treeData"
:props="{
children: 'children',
label: 'label',
hasChildren: 'hasChildren'
}"
:lazy="true"
:load="loadNode"
@node-click="handleNodeClick"
@node-contextmenu="handleNodeMenuClick"
:accordion="false"
:highlight-current="true"
node-key="id"
>
<template #default="{ node, data }">
<el-dropdown
trigger="contextmenu"
:ref="'dropdown'+ node.data.id "
@visible-change="(visible) => handleVisibleChange(node, visible)"
>
<span class="el-dropdown-link">
{{ node.label }}
</span>
<template
#dropdown
v-if="currentDrop && currentDrop == node.data.id"
>
<el-dropdown-menu>
<el-dropdown-item
:disabled="isNewProject"
@click="handleModal"
>
新建
</el-dropdown-item>
<el-dropdown-item
:disabled="!nodeIdFlag"
@click="deleteNodes"
>
删除
</el-dropdown-item>
<el-dropdown-item
@click="renameNode"
:disabled="!displayNameFlag"
>
重命名
</el-dropdown-item>
<el-dropdown-item
:disabled="!descriptionFlag"
@click="handleMessageModal"
>
属性
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
</el-tree>
函数签名:
loadNode(node, resolve):这是懒加载函数的标准形式,接收两个参数,node 是当前需要加载子节点的树节点,resolve 是一个回调函数,用于将加载的子节点数据“解决”并填充到树节点中。
检查节点属性:
let flag = node.data?.isAbstract:尝试从当前节点的数据中获取 isAbstract 属性,用于判断节点是否是抽象的。
条件判断:
if (node.level >= 2 && !flag):如果节点的层级大于或等于2且不是抽象节点,则执行内部的代码块。
定义变量:
let nodeClassToReturn = ‘Object’:定义一个变量 nodeClassToReturn 并赋值为 ‘Object’,这可能是用来指定要返回的节点类型。
发起 API 请求:
let api = new ModellingServiceApi();:实例化一个 API 服务类 ModellingServiceApi。
let res = api.browseNode(…):调用 browseNode 方法发起 API 请求,请求参数包括会话 ID (sid)、节点 ID (nodeid)、返回的节点类型 (nodeClassToReturn) 等。
处理响应数据:
let arr = JSON.parse(JSON.stringify(res)):将 API 响应结果转换成数组形式。
arr.map(…):遍历数组,对每个节点对象进行处理,如设置 id 和 label。
过滤和设置子节点:
let tempArr = arr.filter(…):过滤数组,排除那些浏览名称以 “#” 开头的节点。
node.children = tempArr:将过滤后的数组赋值给当前节点的 children 属性,表示子节点列表。
使用 resolve 回调:
return resolve(res):调用 resolve 回调函数并传入子节点数据,完成懒加载过程。
错误处理:
catch(err){ resolve([]) }:如果请求失败或发生错误,调用 resolve 并传入空数组,表示没有子节点。
其他逻辑分支:
if(!flag){ … } else { … }:根据 flag 的值调用不同的函数来加载子节点数据。
方法调用:
this.getChildrenTreelist(node, resolve) 和 this.loadSubChildrenTree(node, resolve):可能是定义在 Vue 组件中的其他方法,用于根据不同的条件加载子节点数据。
loadNode(node,resolve){
let flag = node.data?.isAbstract
if (node.level >= 2 && !flag) {
let nodeClassToReturn ='Object'
let nodeid = node.data.nodeId
try{
console.time()
let api = new ModellingServiceApi();
let res = api.browseNode({sid:SID,
nodeId:nodeid,
nodeClassToReturn:[nodeClassToReturn],
isForward:true,
limit:999,
page:0});
console.timeEnd()
let arr =[]
arr = JSON.parse(JSON.stringify(res))
arr.map((item)=>{
item.id = item.nodeId
let label =item.name? splitLastHyphen(item.name,'-'):""
item.label =label[0]
this.hasChildList.map((idx)=>{
item.data = idx
})
})
let tempArr = arr.filter((item)=>{return item.browseName.charAt(0) !== "#"})
node.children = tempArr
return resolve(res)
// return resolve(data);
}
catch(err){
resolve([])
}
}
if(!flag){
this.getChildrenTreelist(node,resolve)
}else{
this.loadSubChildrenTree(node,resolve)
}
},
注意事项:在loadNode() 方法中不要轻易使用 async await 会造成UI绘制异常卡顿
在 loadNode() 方法中使用 async/await 与避免使用它取决于具体的应用场景和性能考虑。async/await 是 JavaScript 中处理异步操作的一种非常直观和方便的方式,它建立在 Promise 之上,使得异步代码的写法看起来更像是同步的。然而,在某些情况下,尤其是在性能敏感的环境下,比如懒加载大量数据时,不使用 async/await 可能更合适。以下是一些考虑点:
避免额外的微任务调度:
async/await 在内部可能会创建额外的微任务,这可能会稍微增加调度的开销。在懒加载树节点时,如果对性能要求极高,可能需要避免这种开销。
减少 Promise 解决的复杂性:
使用 async/await 时,每个 await 表达式都会创建一个新的 Promise。在处理大量数据时,这可能会导致额外的内存使用和垃圾回收压力。
保持回调的简洁性:
当使用 async/await 时,错误处理通常使用 try…catch 语句。如果 loadNode() 方法已经非常复杂,添加额外的错误处理可能会增加代码的复杂性。
避免阻塞异步执行队列:
在某些情况下,如果 loadNode() 方法中包含多个 await 调用,它们会按顺序执行,这可能会阻塞异步执行队列,延迟其他异步操作的执行。
使用传统的 Promise 链:
不使用 async/await 的话,可以继续使用 Promise 链式调用(thenable chains),这种方式在处理大量异步操作时可能更加灵活和高效。
保持代码的一致性:
如果你的项目或团队倾向于使用传统的 Promise 风格,那么在 loadNode() 方法中保持一致性可能更有利于代码的可维护性。
避免在事件循环中的不必要等待:
await 会在等待 Promise 解决时暂停函数的执行,这在某些情况下可能会导致不必要的等待,尤其是在主线程上,这可能会影响 UI 的响应性。
使用同步代码的错觉:
async/await 让异步代码看起来像同步代码,这可能会误导开发者忽视异步操作的真正性质,从而在性能优化时忽视了异步操作的开销。
考虑错误处理的开销:
使用 async/await 时,错误处理需要使用 try…catch,这可能会在编译时引入额外的开销,尤其是在处理大量数据时。
避免在 Web Workers 中使用:
如果你打算将 loadNode() 方法中的逻辑放在 Web Worker 中执行,使用 async/await 可能不太合适,因为 Web Workers 环境中不支持 async/await。
总之,是否在 loadNode() 方法中使用 async/await 应根据具体情况和性能要求来决定。如果性能是关键考虑因素,并且你正在处理大量数据,可能需要考虑使用传统的 Promise 或其他异步模式来避免潜在的性能问题。
以上就是文章全部内容了,如果喜欢这篇文章的话,还希望三连支持一下,感谢!