当前位置: 首页 > news >正文

Vue3可媲美Element Plus Tree组件开发之append节点

在前面的章节,我们完成了可媲美Element Plus Tree组件的基本开发。通过实现各种计算属性,tree数据状态变化引起的视图更新被计算属性所接管了,无需我们再手动做各种遍历、查找以及手动监听操作,这样后续开发高级功能变得易如反掌啦!!

在这里插入图片描述

看下提供给用户的vitepress文档说明:

在这里插入图片描述

在这里插入图片描述

操作演示:

在这里插入图片描述

前面我们实现了几个计算属性:

  • index

    节点在扁平化列表中的位置索引

  • length

    父节点的所有子孙节点的长度

  • visibleLength

    可见子孙节点的长度

  • lineLength

    参照线的长度

这些计算属性在新增一个节点,尤其是子节点时都会被影响到,触发重新计算以保证前面实现的基本功能是完好的。而无需我们在实现新增节点时再去兼顾基础功能,这就是Vue3 composition api的计算属性的魅力,让复杂的功能变得简单,组件的开发者只需要把关注点放到影响计算属性变化的数据上即可,Life is so easy!

新增类型、接口

定义ts的类型和接口,注意给用户提供的接口一定要遵循“迪米特法则”。

在这里插入图片描述

核心插入逻辑

/*** 新增顶级节点* @param child 要新增的叶子节点* @param data 扁平化节点列表* @param treeData 结构化节点树* @param optionProps 组件配置选项*/
export function appendTop(child: ILeafNode, data: IFlatTreeNode[], treeData: ITreeNode[], optionProps: OptionProps) {// 节点id命名逻辑:如果指定了就用用户指定的,否则按照列表长度生成child.id = child.id || 'id-' + (data.length + 1)// 从新增节点拷贝数据作为original child nodeconst ocNode = { ...child }// 扁平化new child nodeconst ncNode = {...child,level: 1,isLeaf: true,originalNode: ocNode} as IFlatTreeNode// 要插入的位置为列表最后const insertIndex = data.length// 绑定新插入的扁平化节点的前置节点ncNode.prev = data[data.length - 1]// 对新的扁平化节点进行初始化initFlatTreeNode(ncNode, optionProps)// 原始树结构中新增节点,注意!!操作的是响应式数据ref(treeData).value.push(ocNode as never)// 扁平化列表中插入新节点ref(data).value.splice(insertIndex, 0, ncNode as never)
}function initFlatTreeNode(node: IFlatTreeNode, optionProps: OptionProps) {.../*** 给扁平化节点绑定新增子节点的方法* @param child 新增的子节点* @param data 扁平化列表*/node.append = (child: ILeafNode, data: IFlatTreeNode[]) => {// 同新增一级节点child.id = child.id || 'id-' + (data.length + 1)// 当前节点原始节点const oNode = node.originalNode// 新增节点原始节点const ocNode = { ...child }// 新增节点扁平化节点const ncNode = {...child,parent: node, // 绑定父节点level: node.level + 1,isLeaf: true,originalNode: ocNode} as IFlatTreeNode// 计算插入位置const insertIndex = calcInsertIndex(node)// 插入到最后的情况下,设置前置节点if (insertIndex === data.length) {ncNode.prev = data[data.length - 1]} else {// 插入到中间,绑定prev的逻辑,把prev链接起来const next = data[insertIndex]ncNode.prev = next.prev// 注意操作的是响应式对象,以确保可以触发index属性重新计算!!ref(next).value.prev = ncNode as never}// 初始化扁平化节点initFlatTreeNode(ncNode, optionProps)// 通过响应式对象获取其操作对象const oNodeVal = ref(oNode).valueconst nodeVal = ref(node).valueconst childrenName = optionProps.childrenName as 'children'// 对原先的叶子节点进行设置和初始化,变为非叶子节点if (!oNodeVal[childrenName]) {oNodeVal[childrenName] = []initParentNode(oNode, optionProps)nodeVal.isLeaf = false}// 插入到原始结构化节点oNodeVal[childrenName].push(ocNode as never)// 所在的节点将其展开(如果折叠的话)oNodeVal.expanded = truenodeVal.expanded = true// 插入到扁平化节点列表ref(data).value.splice(insertIndex, 0, ncNode as never)}
}/*** 插入子节点位置逻辑:如果是叶节点,则为下一个位置,否则要加上子一代节点的长度* @param node*/
function calcInsertIndex(node: IFlatTreeNode): number {return node.index.value + 1 + (node.isLeaf ? 0 : node.originalNode.length!.value)
}

Tree组件模板调整

原先给icon插槽传入的节点参数,不符合迪米特法则,暴露了内部操作属性和方法,规范的做法是拷贝一个副本!!只给用户提供其关心的几个属性,调整为:

在这里插入图片描述

对于一级节点新增操作,我们将对tree组件expose一个可操作的对象,为此把这个对象中要定义的方法抽取到ts接口中,以方便客户端API的使用:

// Tree组件对外导出的方法定义
export interface ExposeProps {appendTop: (newNode: ILeafNode) => void
}

导出逻辑:

在这里插入图片描述

而针对节点的操作,给用户提供的API,包装一个函数来返回要操作的接口:

// 返回节点操作方法的函数
const nodeOperation = (node: IFlatTreeNode): ITreeNodeOperation => {// 注意,这里不应该直接给用户提供node,而是要包成对外公开的ITreeNodeOperation,遵循迪米特法则!!return {append(newNode: ILeafNode) {node.append(newNode, originalFlatData)}}
}

对应的插槽实现的地方:

在这里插入图片描述

相关文章:

  • 高级及架构师高频面试题-基础型
  • Python --NumPy库基础方法(2)
  • 【k8s故障处理篇】calico-kube-controllers状态为“ImagePullBackOff”解决办法
  • Python | Leetcode Python题解之第278题第一个错误的版本
  • 系统架构设计师教程 第4章 信息安全技术基础知识-4.5 密钥管理技术4.6 访问控制及数字签名技术-解读
  • 某量JS逆向
  • 【时时三省】(C语言基础)循环语句while
  • 大模型算法面试题(十二)
  • scp 服务器复制命令
  • redis:清除缓存的最简单命令示例
  • 学习记录——day17 数据结构 队列 链式队列
  • C#中GridControl的数据源双向绑定
  • 双向门控循环神经网络(BiGRU)及其Python和MATLAB实现
  • Redis快速入门(一)
  • Spring Boot中如何实现全链路调用日志跟踪?
  • 【RocksDB】TransactionDB源码分析
  • express.js的介绍及使用
  • IndexedDB
  • JavaScript-Array类型
  • Linux中的硬链接与软链接
  • maya建模与骨骼动画快速实现人工鱼
  • Ruby 2.x 源代码分析:扩展 概述
  • RxJS: 简单入门
  • 阿里云购买磁盘后挂载
  • 开发基于以太坊智能合约的DApp
  • 利用阿里云 OSS 搭建私有 Docker 仓库
  • 马上搞懂 GeoJSON
  • 模仿 Go Sort 排序接口实现的自定义排序
  • 前端技术周刊 2019-01-14:客户端存储
  • 深入浅出Node.js
  • 想使用 MongoDB ,你应该了解这8个方面!
  • 一起来学SpringBoot | 第三篇:SpringBoot日志配置
  • [Shell 脚本] 备份网站文件至OSS服务(纯shell脚本无sdk) ...
  • 积累各种好的链接
  • ​LeetCode解法汇总1410. HTML 实体解析器
  • # 达梦数据库知识点
  • #pragma multi_compile #pragma shader_feature
  • #常见电池型号介绍 常见电池尺寸是多少【详解】
  • (3) cmake编译多个cpp文件
  • (zhuan) 一些RL的文献(及笔记)
  • (附源码)spring boot智能服药提醒app 毕业设计 102151
  • (附源码)springboot教学评价 毕业设计 641310
  • (免费领源码)python#django#mysql公交线路查询系统85021- 计算机毕业设计项目选题推荐
  • (十八)SpringBoot之发送QQ邮件
  • (小白学Java)Java简介和基本配置
  • (轉貼)《OOD启思录》:61条面向对象设计的经验原则 (OO)
  • .NET/C# 推荐一个我设计的缓存类型(适合缓存反射等耗性能的操作,附用法)
  • .Net的DataSet直接与SQL2005交互
  • .NET多线程执行函数
  • .NET构架之我见
  • .NET简谈设计模式之(单件模式)
  • .NET平台开源项目速览(15)文档数据库RavenDB-介绍与初体验
  • .net中应用SQL缓存(实例使用)
  • @ 代码随想录算法训练营第8周(C语言)|Day57(动态规划)
  • @autowired注解作用_Spring Boot进阶教程——注解大全(建议收藏!)