首页 文章 精选 留言 我的

精选列表

搜索[开源桌面],共10000篇文章
优秀的个人博客,低调大师

个人开源框架,技术点、封装、二次开发,涵盖全新 ORM、Excel、第三方组件监听

wu-framework-parent 介绍 wu-framework-parent是一款由Java语言开发的框架,目标不写代码单数却能完成功能。框架涵盖无赖ORM(wu-database-lazy-starter)、仿生组件、easy框架系列【Easy-Excel、easy-listener、easy-upsert】授权框架(wu-framework-authorization)、Web框架、平台操作组件、层组件抽象 仿生 模块 所属层级 描述 wu-bionic-language 语言模块 语言解析,tts wu-bionic-memory 记忆内存 记忆断点 wu-bionic-think 反思 反思模块 Easy系列 Excel导入导出、文件导入导出 基于第三方组件的upsert操作 模块 所属层级 描述 wu-easy-upsert-core sink sink基础模块 wu-easy-upsert-elasticsearch-sink sink es数据存储 wu-easy-upsert-h2-sink sink h2数据存储 wu-easy-upsert-hbase-sink sink hbase数据存储 wu-easy-upsert-influxdb-sink sink influxdb数据存储 wu-easy-upsert-kafka-sink sink kafka数据存储 wu-easy-upsert-mysql-sink sink mysql数据存储 wu-easy-upsert-pulsar-sink sink pulsar数据存储 wu-easy-upsert-redis-sink sink redis数据存储 wu-easy-upsert-redisearch-sink sink redisearch数据存储 wu-easy-upsert-spring-boot-autoconfigure sink sink配置文件 wu-easy-upsert-starter sink starter包 第三方组件监听 模块 所属层级 描述 wu-easy-kafka-listener listener kafka数据监听 wu-easy-listener-core 核心 核心模块 wu-easy-listener-spring-boot-autoconfigure 配置 配置 wu-easy-listener-starter listener starter wu-easy-mysql-binlog-listener listener binlog数据监听 wu-easy-mysql-listener listener mysql数据查询监听 wu-easy-pulsar-listener listener pulsar监听 wu-easy-redis-listener listener redis数据监听 wu-easy-rocketmq-listener listener rocket数据监听 授权 模块 所属层级 描述 wu-authorization-client-starter 客户端 客户端 wu-authorization-common 共用组件 共用组件 wu-authorization-server-simple 模版案例 模版案例 wu-authorization-server-starter 服务端 服务端 wu-authorization-spring-boot-autoconfigure 配置 配置

优秀的个人博客,低调大师

给 Antd Table 组件编写缩进指引线、子节点懒加载等功能,如何二次封装开源组件?

在业务需求中,有时候我们需要基于 antd 之类的组件库定制很多功能,本文就以我自己遇到的业务需求为例,一步步实现和优化一个树状表格组件,这个组件会支持: ✨ 每个层级 缩进指示线 ✨ 远程 懒加载子节点 ✨ 每个层级支持 分页 本系列分为两篇文章,这篇只是讲这些业务需求如何实现。 而下一篇,我会讲解怎么给组件也设计一套简单的插件机制,来解决代码耦合,难以维护的问题。 功能实现 层级缩进线 antd 的 Table 组件默认是没有提供这个功能的,它只是支持了树状结构: consttreeData=[ { function_name:`ReactTreeReconciliation`, count:100, children:[ { function_name:`ReactTreeReconciliation2`, count:100 } ] } ] 展示效果如下: antd-table 可以看出,在展示大量的函数堆栈的时候,没有缩进线就会很难受了,业务方也确实和我提过这个需求,可惜之前太忙了,就暂时放一边了。😁 参考 VSCode 中的缩进线效果,可以发现,缩进线是和节点的层级紧密相关的。 vscode 比如 src 目录对应的是第一级,那么它的子级 client 和 node 就只需要在 td 前面绘制一条垂直线,而 node 下的三个目录则绘制两条垂直线。 第 1 层:| text 第 2 层:|| text 第 3 层:||| text 只需要在自定义渲染单元格元素的时候,得到以下两个信息。 当前节点的层级信息。 当前节点的父节点是否是展开状态。 所以思路就是对数据进行一次递归处理,把层级写在节点上,并且要把父节点的引用也写上,之后再通过传给 Table 的 expandedRowKeys 属性来维护表格的展开行数据。 这里我是直接改写了原始数据,如果需要保证原始数据干净的话,也可以参考 React Fiber 的思路,构建一颗替身树进行数据写入,只要保留原始树节点的引用即可。 /** *递归树的通用函数 */ consttraverseTree=( treeList, childrenColumnName, callback )=>{ consttraverse=(list,parent=null,level=1)=>{ list.forEach(treeNode=>{ callback(treeNode,parent,level); const{[childrenColumnName]:next}=treeNode; if(Array.isArray(next)){ traverse(next,treeNode,level+1); } }); }; traverse(treeList); }; functionrewriteTree({dataSource}){ traverseTree(dataSource,childrenColumnName,(node,parent,level)=>{ //记录节点的层级 node[INTERNAL_LEVEL]=level //记录节点的父节点 node[INTERNAL_PARENT]=parent }) } 之后利用 Table 组件提供的 components 属性,自定义渲染 Cell 组件,也就是 td 元素。 constcomponents={ body:{ cell:(cellProps)=>( <TreeTableCell {...props} {...cellProps} expandedRowKeys={expandedRowKeys} /> ) } } 之后,在自定义渲染的 Cell 中,只需要获取两个信息,只需要根据层级和父节点的展开状态,来决定绘制几条垂直线即可。 constisParentExpanded=expandedRowKeys.includes( record?.[INTERNAL_PARENT]?.[rowKey] ) //只有当前是展示指引线的列且父节点是展开节点才会展示缩进指引线 if(dataIndex!==indentLineDataIndex||!isParentExpanded){ return<tdclassName={className}>{children}</td> } //只要知道层级就知道要在 td 中绘制几条垂直指引线举例来说: //第 2 层:|| text //第 3 层:||| text constlevel=record[INTERNAL_LEVEL] constindentLines=renderIndentLines(level) 这里的实现就不再赘述,直接通过绝对定位画几条垂直线,再通过对 level 进行循环时的下标 index 决定 left 的偏移值即可。 效果如图所示: 缩进线 远程懒加载子节点 这个需求就需要用比较 hack 的手段实现了,首先观察了一下 Table 组件的逻辑,只有在有 children 的子节点上才会展示「展开更多」的图标。 所以思路就是,和后端约定一个字段比如 has_next,之后预处理数据的时候先遍历这些节点,加上一个假的占位 children。 之后在点击展开的时候,把节点上的这个假 children 删除掉,并且把通过改写节点上一个特殊的 is_loading 字段,在自定义渲染 Icon 的代码中判断,并且展示 Loading Icon。 又来到递归树的逻辑中,我们加入这样的一段代码: functionrewriteTree({dataSource}){ traverseTree(dataSource,childrenColumnName,(node,parent,level)=>{ if(node[hasNextKey]){ //树表格组件要求next必须是非空数组才会渲染「展开按钮」 //所以这里手动添加一个占位节点数组 //后续在onExpand的时候再加载更多节点并且替换这个数组 node[childrenColumnName]=[generateInternalLoadingNode(rowKey)] } }) } 之后我们要实现一个 forceUpdate 函数,驱动组件强制渲染: const[_,forceUpdate]=useReducer((x)=>x+1,0) 再来到 onExpand 的逻辑中: constonExpand=async(expanded,record)=>{ if(expanded&&record[hasNextKey]&&onLoadMore){ //标识节点的loading record[INTERNAL_IS_LOADING]=true //移除用来展示展开箭头的假children record[childrenColumnName]=null forceUpdate() constchildList=awaitonLoadMore(record) record[hasNextKey]=false addChildList(record,childList) } onExpandProp?.(expanded,record) } functionaddChildList(record,childList){ record[childrenColumnName]=childList record[INTERNAL_IS_LOADING]=false rewriteTree({ dataSource:childList, parentNode:record }) forceUpdate() } 这里 onLoadMore 是用户传入的获取更多子节点的方法, 流程是这样的: 节点展开时,先给节点写入一个正在加载的标志,然后把子数据重置为空。这样虽然节点会变成展开状态,但是不会渲染子节点,然后强制渲染。 在加载完成后赋值了新的子节点 record[childrenColumnName] = childList 后,我们又通过 forceUpdate 去强制组件重渲染,展示出新的子节点。 需要注意,我们递归树加入逻辑的所有逻辑都在 rewriteTree 中,所以对于加入的新的子节点,也需要通过这个函数递归一遍,加入 level, parent 等信息。 新加入的节点的 level 需要根据父节点的 level 相加得出,不能从 1 开始,否则渲染的缩进线就乱掉了,所以这个函数需要改写,加入 parentNode 父节点参数,遍历时写入的 level 都要加上父节点已有的 level。 functionrewriteTree({ dataSource, //在动态追加子树节点的时候需要手动传入parent引用 parentNode=null }){ //在动态追加子树节点的时候需要手动传入父节点的level否则level会从1开始计算 conststartLevel=parentNode?.[INTERNAL_LEVEL]||0 traverseTree(dataSource,childrenColumnName,(node,parent,level)=>{ parent=parent||parentNode; //记录节点的层级 node[INTERNAL_LEVEL]=level+startLevel; //记录节点的父节点 node[INTERNAL_PARENT]=parent; if(node[hasNextKey]){ //树表格组件要求next必须是非空数组才会渲染「展开按钮」 //所以这里手动添加一个占位节点数组 //后续在onExpand的时候再加载更多节点并且替换这个数组 node[childrenColumnName]=[generateInternalLoadingNode(rowKey)] } }) } 自定义渲染 Loading Icon 就很简单了: //传入给Table组件的expandIcon属性即可 exportconstTreeTableExpandIcon=({ expanded, expandable, onExpand, record })=>{ if(record[INTERNAL_IS_LOADING]){ return<IconLoadingstyle={iconStyle}/> } } 功能完成,看一下效果: 远程懒加载 每个层级支持分页 这个功能和上一个功能也有点类似,需要在 rewriteTree 的时候根据外部传入的是否开启分页的字段,在符合条件的时候往子节点数组的末尾加入一个占位 Pagination 节点。 之后在 column 的 render 中改写这个节点的渲染逻辑。 改写 record: functionrewriteTree({ dataSource, //在动态追加子树节点的时候需要手动传入parent引用 parentNode=null }){ //在动态追加子树节点的时候需要手动传入父节点的level否则level会从1开始计算 conststartLevel=parentNode?.[INTERNAL_LEVEL]||0 traverseTree(dataSource,childrenColumnName,(node,parent,level)=>{ //加载更多逻辑 if(node[hasNextKey]){ //树表格组件要求next必须是非空数组才会渲染「展开按钮」 //所以这里手动添加一个占位节点数组 //后续在onExpand的时候再加载更多节点并且替换这个数组 node[childrenColumnName]=[generateInternalLoadingNode(rowKey)] } //分页逻辑 if(childrenPagination){ const{totalKey}=childrenPagination; constnodeChildren=node[childrenColumnName]||[]; const[lastChildNode]=nodeChildren.slice?.(-1); //渲染分页器,先加入占位节点 if( node[totalKey]>nodeChildren?.length&& //防止重复添加分页器占位符 !isInternalPaginationNode(lastChildNode,rowKey) ){ nodeChildren?.push?.(generateInternalPaginationNode(rowKey)); } } }) } 改写 columns: functionrewriteColumns(){ /** *根据占位符渲染分页组件 */ constrewritePaginationRender=(column)=>{ column.render=functionColumnRender(text,record){ if( isInternalPaginationNode(record,rowKey)&& dataIndex===indentLineDataIndex ){ return<Pagination/> } returnrender?.(text,record)??text } } columns.forEach((column)=>{ rewritePaginationRender(column) }) } 来看一下实现的分页效果: 重构和优化 随着编写功能的增多,逻辑被耦合在 Antd Table 的各个回调函数之中, 指引线的逻辑分散在 rewriteColumns, components中。 分页的逻辑被分散在 rewriteColumns 和 rewriteTree 中。 加载更多的逻辑被分散在 rewriteTree 和 onExpand 中 至此,组件的代码行数也已经来到了 300 行,大概看一下代码的结构,已经是比较混乱了: exportconstTreeTable=(rawProps)=>{ functionrewriteTree(){ //🎈加载更多逻辑 //🔖分页逻辑 } functionrewriteColumns(){ //🔖分页逻辑 //🏁缩进线逻辑 } constcomponents={ //🏁缩进线逻辑 } constonExpand=async(expanded,record)=>{ //🎈加载更多逻辑 } return<Table/> } 有没有一种机制,可以让代码按照功能点聚合,而不是散落在各个函数中? //🔖分页逻辑 constusePaginationPlugin=()=>{} //🎈加载更多逻辑 constuseLazyloadPlugin=()=>{} //🏁缩进线逻辑 constuseIndentLinePlugin=()=>{} exportconstTreeTable=(rawProps)=>{ usePaginationPlugin() useLazyloadPlugin() useIndentLinePlugin() return<Table/> } 没错,就是很像 VueCompositionAPI 和 React Hook 在逻辑解耦方面所做的改进,但是在这个回调函数的写法形态下,好像不太容易做到? 下一篇文章,我会聊聊如何利用自己设计的插件机制来优化这个组件的耦合代码。 记得关注后加我好友,我会不定期分享前端知识,行业信息。2021 陪你一起度过。 感谢大家 欢迎关注 ssh,前端潮流趋势、原创面试热点文章应有尽有。 记得关注后加我好友,我会不定期分享前端知识,行业信息。2021 陪你一起度过。 点赞并分享给你的朋友是最好的支持~ 本文分享自微信公众号 - 前端从进阶到入院(code_with_love)。 如有侵权,请联系 support@oschina.cn 删除。 本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册