LionCubFrontEnd / Chocolate-2022

⚡用此来记录在 2022 年学习与成长,自我驱动 | 坚持 | 热爱 | 积极,座右铭:学如逆水行舟,不进则退❤️
MIT License
8 stars 0 forks source link

关于 antd@4 之后 tree 树形控件不能横向排列这件事 #3

Open Chocolate1999 opened 2 years ago

Chocolate1999 commented 2 years ago

遇到这个问题,首先还是搜一下,搜到了相关官方的 issues,如下链接:

树形控件tree需求一个可调节子节点可竖向排列还是横向排列 #8751

在 17 年就有人提及需要支持横向排列,因为这个树形如果按照竖向的话,数据一多了,就显得很长。

当时好像就没有解决这个问题,也看了 antd 之前的版本发现都没有这个配置项,不过倒是找到了相关实现方式,如下链接:

修改antd tree组件,使其横向排列

其实就是通过修改样式来做,有了这个思路,我也打算对最新版的 antd 操作一番,不过发现 dom 结构已经变了,不像是之前有 ul li 这种结构了,最底层的 child 节点们外层没有包裹的元素,每一个都是单独的 div。

此时,how to gao?

到底怎么搞?

...

开始寻找

于是,开始了两天的各种尝试,比如说,看到了这篇博客:

React+Ant Design+Tree渲染树形菜单(机构树)

BFS 和 DFS

我也想着应该也可以自定义渲染树,当时还画了一张草图,准备动用一些算法,如下图:

想着对于 ReactNode 来说必须要嵌套才行,所以单纯对于树结构 BFS 不太行,感觉还是得要递归才行,又想到了 DFS,但是这个遍历方式不一样,我又画了一张草图:

感觉这像是 BFS 和 DFS 结合?

所以我还是得判断一下当前节点有没有孩子节点,如果有的话就递归下去,并且当前节点也要显示,于是就有了下面大概的伪代码,核心部分就是这个,也是用的之前 antd@3 的方式,拿出了 TreeNode,不过在 antd@4 之后使用控制台会有提醒不建议使用,推荐使用 treeData。

import { Tree } from 'antd';
const { TreeNode } = Tree;

// const data = [...];

// 核心部分
const renderTree = (data: any) => {
    if (data && data.length && data[0].children) {
        return data.map((item: any) => {
            return (
                <TreeNode title={item.title} key={item.key}>
                    {item.children && renderTree(item.children)}
                </TreeNode>
            );
        });
    } else {
        return (
            <TreeNode>
                {data.map((item: any) => {
                    return (
                        <TreeNode
                            title={item.title}
                            key={item.key}
                            className={s.treeNodeChild}
                        ></TreeNode>
                    );
                })}
            </TreeNode>
        );
    }
};

const Test = () => {
    return (
        <Tree>
            {renderTree(data)}
        </Tree>
    )
}

export xxx;

我以为这种方式完美解决了,当时思路也很清晰,就是判断没有孩子节点的情况,那时候我可以获得上一个节点的所有孩子节点,把这些孩子节点包在一个 div 里面,然后给这个 div 加一个 flex 就完美解决问题。

可是!

如果给 TreeNode 包裹一个 div 居然就失效了...

孩子节点不会渲染,包括这个 div,试了好几次还是不行,于是这个方案放弃了。

安装两个版本

这个是我突发奇想,我想着既然 antd@3 过去可以通过样式来解决横向排列问题,那么是不是我可以安装两个 antd 版本来解决,当时就搜到了这篇博客,链接如下:

npm安装同一个包的2个版本

我在 codesandbox 中尝试了一下,会有样式问题,毕竟现在版本和之前版本 dom 结构都不一样了,所以如果之前全局导入了 antd@4 的样式,对于我这个 antd@3 版本的就不太行了。

当时也搜到了这篇博客:

安装两个不同antd 版本、修改antd样式前缀

还要改前缀,对当时的我来说还是感觉些许麻烦,还是放弃了这个方式,毕竟共存组件两个版本也不太好,毕竟我不是旧版本升级新版本,我新的又继续往老的兼容感觉有点反调了,想想还是得再找找方式。

自定义渲染节点

后续又翻阅了文档,基本上把每个字段都阅读了一遍,后面发现了 titleRender 这个配置项,于是查阅了一些使用,找了一篇博客样例,如下:

antd的tree控件怎么渲染特定节点的样式?

titleRender 自定义渲染节点 (nodeData) => ReactNode

不过这个好像并不能解决我的问题,我是想把没有孩子节点的所有子节点包括在一起,然后加个样式,这个遍历得到的每一个节点,也许是自己使用方式不对,折腾了一会还是没搞出来于是放弃这个方式了。

寻找 npm 包

我想应该会有和我一样需求的人吧,于是我去 npm 去搜索了一些关于 antd tree,我安装了一些,不过好像都没解决这个问题,难道真要我自己手写一个了?

别吧,白嫖多香...

尝试更新 UI

期间我看了一下 material-ui 以及 antd-pro 是否可以,但是 material-ui 风格和国内还是有点区别,而产品的原型又是根据 antd 来的,带有 checkbox,而且一些选中和部分选中情况,material-ui 又没有,所以不考虑了。

这时候,我不知道咋的一下就想到了 vue,而 vue 我当时学习时候就用过 element-ui,好家伙,我立马打开了官网,搜了一下 Tree,好家伙,好家伙,UI 风格居然没多大差别。

我又老规矩 F12 看看元素是怎样的,先在浏览器端改改样式,我发现居然可以!

既然之前还想着 UI 共存两个版本了,那么共存两个 UI 又多大回事呢是吧(逃)

不过为了满足这个特定的需求,不得不妥协加入两个 UI 库,其实也还好,不用担心样式问题,element-ui 样式前缀都会有一个 el。

不过,这时候一想这不是 Vue 的嘛, React 能行嘛,翻了翻,诶,找到了 element-react,在 codesandbox 上写了一个 demo,地址放在这吧:

element-ui-tree-demo

实现效果如下:

算是解决了这个问题吧。

总结

怎么说呢,最后还是跳出来了,没有一直卡在问题里面,但可能不是一种比较好的解决方式?

至于在大的版本切换之时,将原本的渲染结构重构了一遍这个过程,不太清楚 ul li 结构变成全 div 是为了考虑啥? 性能方面嘛?之前好像在官方文档哪块地方看到过,不过既然改都改了,那么对于这个业务需求暂时就这样解决了。

在此记录一下自己的解决方案,毕竟都换了 UI 库了,不知道各位有没有更好的解决方式,愿意一起探讨。

学如逆水行舟,不进则退。

Chocolate1999 commented 2 years ago

B 站视频已录制,传送门:https://www.bilibili.com/video/BV1AT4y1Q78d

APIS-X commented 2 years ago

@Chocolate1999 ant升级后Tree组件的节点全部用div标签打平了,而且节点的class没有做任何标识,导致叶子节点无法通过样式横排。其实通过标识有子节点的node节点的class,然后通过样式控制即可。ant的Tree是对rc-tree的二次封装,treeData的数据结构中通过设置isLeaf或者className属性即可达到目的,详情可以查看TreeNode的属性API 或者rc-tree的API https://tree-react-component.vercel.app/

Chocolate1999 commented 2 years ago

@Chocolate1999 ant升级后Tree组件的节点全部用div标签打平了,而且节点的class没有做任何标识,导致叶子节点无法通过样式横排。其实通过标识有子节点的node节点的class,然后通过样式控制即可。ant的Tree是对rc-tree的二次封装,treeData的数据结构中通过设置isLeaf或者className属性即可达到目的,详情可以查看TreeNode的属性API 或者rc-tree的API https://tree-react-component.vercel.app/

可以

vinoMamba commented 2 years ago

@Chocolate1999 ant升级后Tree组件的节点全部用div标签打平了,而且节点的class没有做任何标识,导致叶子节点无法通过样式横排。其实通过标识有子节点的node节点的class,然后通过样式控制即可。ant的Tree是对rc-tree的二次封装,treeData的数据结构中通过设置isLeaf或者className属性即可达到目的,详情可以查看TreeNode的属性API 或者rc-tree的API https://tree-react-component.vercel.app/

@APIS-X 请问如何通过样式来实现效果。能提供下思路吗?

vinoMamba commented 2 years ago

按照上面的思路可以这么做:

  1. .ant-tree-list-holder-inner 的包裹元素上去掉 flex属性
  2. 将所有叶子节点的display 改成 inline-flex
Chocolate1999 commented 2 years ago

按照上面的思路可以这么做:

  1. .ant-tree-list-holder-inner 的包裹元素上去掉 flex属性
  2. 将所有叶子节点的display 改成 inline-flex

是的,我在 stackblitz 写了一个 demo

https://stackblitz.com/edit/vitejs-vite-phmgsx?file=src/App.tsx

image

原来可以这样子,谢谢各位大佬 @APIS-X @vinoMamba