0.3.6 • Published 1 year ago
wasabi-tree v0.3.6
树组件开发文档
1.开发目的
- 实现对树组件的增删改查
- 实现树组件的拖动,停靠
- 实现树组件的父子勾选,单选,展开
- 实现树组件的节点图标,节点,虚线,皮肤的自定义
- 实现树组件通过 url 来加载数据
- 子节点数据可以异步加载
- 通过数据遍历算法优化及虚拟列表来实现大量数据时高性能渲染
- 提供外部调用 api,获取树组件各类数据
- 实现容器高度变化自适应
使用方式
2.1 下载方式
npm install wasabi-tree yarn add wasabi-tree
2.2 引入方式
1 data 形式
import Tree from "wasabi-tree";
import "wasabi-tree/lib/index.css";
function Demo(props) {
return <Tree data={props.data}></Tree>;
}
2 url 形式
import Tree from "wasabi-tree";
import "wasabi-tree/lib/index.css";
function Demo(props) {
return <Tree url={props.url}></Tree>;
}
2.3 树属性
属性名 | 类型 | 说明 | 默认值 |
---|---|---|---|
name | string | 树名称 | null |
idField | string | 指定哪个字段是节点的 id | id |
parentField | string | 指定哪个字段是节点的 pId | pId |
textField | string | 指定哪个字段是节点的 text | text |
childrenField | string | 指定哪个字段是节点的 children | children |
url | string | 1.后台查询地址,第一次自动查询 2.节点展开时如果asyncAble为true,而oAsync函数为空,则自动根据这个url地址查询 | null |
params | object | 向后台传输的额外参数 | null |
dataSource | string | 有 url 参数时的返回的数据源中哪个属性作为数据源,可以分层比如data.list | data |
headers | array | 请求时传的表头参数 | null |
data | array | 父组件传的固定数据 | null |
isSimpleData | bool | 是否使用简单的数据格式:。目的是在前端将扁平化数据转成树状结构 | false |
dottedAble | bool | 是否有虚线 | true |
selectAble | bool | 是否允许勾选 | false |
checkStyle | oneOf("checkbox", "radio", func) | 单选还是多选,可以通过函数返回自定义组件,func(row){retrun node;} 注意: 宽度20px,高度 30px | checkbox |
checkType | object | 勾选对于父子节点的关联关系[y]代表选中,[n]代表取消 [p]父节点 [s]代表子节点 | { "y": "ps", "n": "ps" } |
radioType | oneOf("level", "all") | 单选时影响的层级[level]同级 [all]整个树 | all |
renameAble | bool | 是否允许重命名 | false |
removeAble | bool | 是否允许移除 | false |
draggAble | bool | 是否允许拖动 | false |
dropAble | bool | 是否允许停靠 | false |
dropType | array | 停靠模式 | null,["before","in","after" |
asyncAble | bool | 展开节点是否可以异步加载数据 | false |
textFormatter | func(row) | 自定义节点文本样式函数 例子:(row)=>{return <div className="red">{row.text}</div> | null |
2.4 事件
属性名 | 类型 | 说明 | 参数 | 返回值 |
---|---|---|---|---|
onClick | func | 单击的事件 | id,text,row | null |
onDoubleClick | func | 双击事件 | id,text,row | null |
onCheck | func | 勾选/取消勾选事件 | isChecked, id, text, row | null |
onExpand | func | 展开/折叠事件 | open, id, text, row | null |
onAdd | func | 新增事件 | id, text, parentNode | null |
onRename | func | 重命名事件 | id, text, row, newText | null |
onRemove | func | 删除事件 | id, text, row | null |
onContextMenu | func | 自定义右键菜单视图 | id, text, row,event | null |
onDrag | func | 拖动事件 | id, text, row | null |
onDrop | func | 停靠事件 | dragNode(移动节点),dropNode(停靠节点)dragType(停靠方式) | null |
onAsync | func | 节点异步查询,为 null,则会通过 url 来处理 | id, text, row | 返回值即异步加载后节点数据 |
onAsync | func | 节点异步查询,为 null,则会通过 url 来处理 | id, text, row | 返回值即异步加载后节点数据 |
loadSuccess | func | 有 url 时请求成功后的事件,用于加工数据 | data | 返回加工后的数据 |
beforeAdd | func | 新增前事件 | id, text, row | true(同意),false(不同意) |
beforeRemove | func | 删除前事件 | id, text, row | true(同意),false(不同意) |
beforeRename | func | 重命名前事件 | id, text, row | true(同意),false(不同意) |
beforeDrag | func | 拖动前事件 | id, text, row | true(同意),false(不同意) |
beforeDrop | func | 停靠前事件 | dragNode(移动节点), dropNode(停靠节点), dragType(停靠方式 | true(同意),false(不同意) |
2.5 组件方法(ref)
属性名 | 类型 | 说明 | 参数 | 返回值 |
---|---|---|---|---|
findNode | func | 获取某个节点 | id | node |
findParents | func | 获取某个节点所有父节点包括自身 | id | nodes |
getData | func | 获取所有节点 | null | data |
getChecked | func | 获取勾选节点 | null | isCheckedData |
setChecked | func | 设置勾选节点 | id,isChecked | null |
setDisabled | func | 设置不可操作 | id,disabled | null |
clearChecked | func | 清除全部勾选节点 | null | null |
checkedAll | func | 勾选全部节点 | null | data |
selectNode | func | 设置节点单击选中,并且滚动到此处,祖先节点全部展开 | id | null |
remove | func | 删除节点,可以传数组,代表删除多个 | id 或者id,id | null |
removeAll | func | 删除所有节点 | null | null |
append | func | 追加某个节点,id 为空时即为更新全部 | children,id | null |
update | func | 更新某个节点,或者某组node,node | nodes | null |
updateAll | func | 更新整个树 | data | null |
filter | func | 过滤节点,过滤的视图请自行设计 | value | null |
adjust | func | 重新调整容器,适应容器高度发生变化 | null | null |
2.6 节点 Node 属性
属性名 | 类型 | 说明 | 默认值 |
---|---|---|---|
isParent | bool | 是否是父节点 | null |
id | string | key 值,如果没有会根据树组件设定的 idField 来转换 | "" |
pId | string | 父节点 key 值 如果没有会根据树组件设定的 parentField 来转换 | "" |
text | string | 节点文本,如果没有会根据树组件设定的 textField 来转换 | "" |
title | string | 提示信息 | "" |
iconCls | string /node | 默认图标,可以是组件 | icon-text |
iconClose | string/node | 父节点关闭图标 可以是组件 | icon-folder |
iconOpen | string/node | 父节点展开图标 可以是组件 | icon-folder |
arrowUnFoldIcon | node | 节点展开的箭头图标组件 | icon-arrow-down |
arrowFoldIcon | node | 节点折叠的箭头图标组件 | icon-arrow-right |
isOpened | bool | 是否处于打开状态 | null |
isChecked | bool | 是否被勾选 | null |
addAble | bool | 是否允许新增 | null |
removeAble | bool | 是否允许删除 | null |
removeIconAble | bool | 是否允许删除图 | null |
renameAble | bool | 是否允许重命名 | null |
renameIconAble | bool | 是否允许重命名图标 | null |
selectAble | bool | 是否允许勾选 | null |
draggAble | bool | 是否允许拖动 | null |
dropAble | bool | 是否允许停靠 | null |
hide | bool | 是否隐藏 | null |
disabled | bool | 是否不可操作 | null |
children | array | 子节点,如果没有会根据树组件设定的 childrenField 来转换 | null |
3 树组件性能设计
3.1 性能策略
3.1.1 虚拟列表
- 容器渲染完成后,拿到可见高度,通过节点行高,得到可见区域的节点个数
- 要渲染的数据是
visibleData
:上部预留区域+可见区域-下部预留区域 - 扁平化后切割数据,得到渲染数据:
visibleData
- 监听容器的滚动事件,得到起始下标,重新计算要渲染的数据,并且进行了节流
3.1.2 树型结构与格式化,扁平化数据时优化算法
- 在 toTreeData 函数中将后端二维表格数据转树型结构时,利用 hash,并且不破坏原始数据结构来优化算法。
- formatTreeNodeData【格式化】与 treeDataToFlatData【扁平化】时:循环与递归过程中不使用浅复制,不创建新的对象,不删除, 只是单纯的 for
3.1.3 格式化数据时,标准化节点数据结构:id,pId,text,children
通过字段属性名,标准化节点这四个字段值,方便对树节点的渲染与操作
3.1.4 格式化数据,增加节点路径属性:_path
并且根据 id 作为 key 保存在 map 数据中hashData
- 对传递的数据先进行预处理,设置节点_path 属性,保存节点在树结构的路径
- 能够通过_path 快速找到节点,及祖先节点,快速操作树节点的勾选,增,删,修改,移动
- 通过_path 字段来确实节点前面空白占位宽度大小,扁平化后依然能显示层级关系
- 通过
hashData
中的_path,方便通过 id 快速找到节点 方便上层组件
3.1.5 除可见数据等少数状态外,整个树结构的数据不参与 State
- 树的全部全部 data,扁平化数据 flatData,筛选后的数据 filterData 等都不参与 State
- 设置异步更新,将多次数据操作合并成一次更新
(父组件可能同时调用几个方法)
- state 只有四个属性:
visibleData,loadingId,clickId,scrollIndex
scrollIndex
为是 selectNode 方法,选中节点要滚动的位置,设置为对象,能够更好的监听变化,防止鼠标滚动后,再设置时值相同的问题
3.1.6 提供通过ref
来操作的多种方法方便父组件操作节点避免更新整个data
- 父组件可以通过,findNode,findParents,getisChecked,getData 等多种方法来获取即时数据状态,
- 父组件可以通过 setChecked,selectNode, append,remove,update,moveIn 等来勾选,选中,追加,删除,更新,移动等节点等多种操作
这样避免父组件更新data,从而导致整体重新计算
3.1.7 缓存所有方法与组件,避免重复渲染,最大限度的优化组件
利用 useCallBack,useReduce,memo 等方式来减少重定义与渲染次数
3.1.8 利用上下文传递透传树组件的属性与方法
利用 context 来缓存树组件本身的属性避属性在节点中一级一级传递
3.1 十万数据量的渲染情况
渲染情况不完全准确:
2.2.1 isSimpleData 为 true 格式化数据平均时长:约155ms
,
2.2.1 树状结格式化数据平均时长:约33ms
2.2.2 扁平化数据平均时长:约25ms
2.2.3 首屏时间:约0.8s
2.2.4 删除节点:约73ms
2.2.4 移动节点:约126ms
4 树组件的设计思路
4.1 树的关系图
@startuml
class func{
公共函数,扁平数据转树状结构
}
class treeFunc{
数据加工
}
class handlerData {
reducer计算
}
class TreeContainer{
树组件的容器,负责数据处理
}
class TreeView{
树组件View渲染,
}
class TreeNode{
树节点,处理节点事件
}
class NodeView{
树节点视图,负责渲染
}
class CheckBox{
复选框
}
class Radio{
单选框
}
class Text{
文本框
}
func-->treeFunc:【isSimpleData为true时toTreeData】扁平数据转树结构
treeFunc-->handlerData:操作数据:加工数据,并格式化,扁平化
handlerData-->TreeContainer:响应树的事件,得到新的状态
TreeContainer-->TreeView:树组件View
TreeView-->TreeNode:树节点
TreeNode-->NodeView:树节点View
NodeView-->CheckBox:复选框
NodeView-->Radio:单选框
NodeView-->Text:文本框
@enduml
4.2 树的数据流图示
4.2.1 状态数据流
@startuml
父组件-->TreeContainer:将父组件传递的【data】
父组件-->TreeContainer:通过父组件传递的url加载后的【data】
父组件-->TreeContainer:其他控制权限的props
getVisibleCount-->TreeContainer:计算可见高度visibleHeight,scrollTop:滚动位置,得到可视区节点个数及下标
TreeContainer-->handlerData:传递gobalData,action,payload用于加工数据
handlerData-->TreeContainer:加工数据,【reduce】计算返回新的state
@enduml
4.2.1 data 加工流程
@startuml
formatTreeNodeData-->handlerData:格式化data,生成id,pId,text,children必需属性, 追加_path属性,
formatTreeNodeData-->handlerData:追加_path属性的渲染与查询
formatTreeNodeData-->handlerData:生成hashData(Map) 方便通过id查询节点
treeDataToFlatData-->handlerData:扁平化数据flatData,并且追加_isLast,_openDescendant 属性 方便渲染虚线
getVisibleData-->handlerData:【reduce】生成新的visibleData,可见数据
@enduml
4.2.1 data 组件数据流
@startuml
TreeContainer-->TreeView:传递visibleData,props,events作为上下文传递给节点,渲染可见的节点
TreeView-->TreeNode:for循环visibleData,渲染具体的节点
TreeNode-->TreeContainer: 设置节点的事件,借助上下文处理数据
TreeNode-->NodeView:渲染节点视图
@enduml
4.3 函数与类的作用
4.3.1 getVisibleCount-获取可见区域节点数量
- 根据容器高度得到可渲染的数据个数 visibleCount
- 得到可见区域的起始结束下标(startIndex,endIndex),
不是了可见数据visibleData的起始下标,因为有上下预留区
4.3.2 getVisibleData-得到可见数据
- 将 data 扁平化得到 flatData
- 得到
visibleData[可见数据]
,filterData【过滤的数据】
,flatData【扁平化数据】
,data[原始可操作数据]
- 得到
sliceBeginIndex[切割的起始下标]
,sliceEndIndex[切割的结束下标]
- 在滚动过程不再扁平化,直接使用上次的 flatData
- 除非有新的 filterValue,或者更新 Data 的结构,再重新扁平化
4.3.3 getData-请求数据
如果传递的 url,进行 fetch 请求,得到传递的数据
4.3.4 func-toTreeData 转成树状结构
- 将简单数据转成树结构
4.3.5 treeFunc-树的操作
- 树节点的寻址
- 树节点的增删改查
- 树节点选中,勾选
- 树节点的移动,停靠
- 树节点的筛选
- 树节点的格式化
- 树节点的扁平化
4.3.6 treeFunc-formatTreeNodeData-格式化数据
- 格式化数据,方便后期树节点的操作
- 通过 idFeild,parentFeild,textFeild,childFeild 得到·
id,pId,text,children
属性 id[key]
,pId[父节点id]
,text[文本】
,children[子节点]
- 生成
_path
属性,即节点的路径,用于寻址及渲染 - 生成
hashData
map,方便通过 id 来查询节点
4.3.7 myReducer-树的 reduce 函数
处理树组件的 state
4.3.8 TreeContainer-树的容器
- 调用
【getVisibleCount】
【showVisibleData】
进行第一次渲染 - 处理树所有的事件,调用
[myReducer]
得到新的 state - 提供 ref 调用的方法,方便获树相关的数据
- 利用上下文将 state,props 传递给
[TreeView]
4.3.9 TreeView-树的视图
- 渲染树组件的节点
4.3.10 TreeNode-树节点容器
- 处理树节点的所有事件,回传给树容器
4.3.11 NodeView,CheckBox,Radio.Text-树节点视图,复选框,单选框
NodeView:渲染树的节点视图 CheckBox:复选框 Radio:单选框 Text:编辑时的文本框
0.2.91
1 year ago
0.3.0
1 year ago
0.3.6
1 year ago
0.3.5
1 year ago
0.2.9
1 year ago
0.2.8
1 year ago
0.3.2
1 year ago
0.3.1
1 year ago
0.3.4
1 year ago
0.3.3
1 year ago
0.2.7
2 years ago
0.2.5
2 years ago
0.2.4
2 years ago
0.2.3
2 years ago
0.2.2
2 years ago
0.2.1
2 years ago
0.2.0
2 years ago
0.1.7
2 years ago
0.1.6
2 years ago
0.1.5
2 years ago
0.1.4
2 years ago
0.1.3
2 years ago
0.1.2
2 years ago
0.1.0
2 years ago