手撸一个Table组件(Table组件不过如此)

news2025/2/26 9:43:10

一、前言

手写Table组件这个文章我一直都想写,今天终于得空来写它了。小编认为Table组件是组件库里"较为复杂"的一个组件,因为它的扩展性非常强,并且它的基础样式如何去写都非常考究,那么今天我就带大家来实现一个基础功能的table组件,废话不多bb,进入正题吧。

二、实现哪些功能?

  • 最基础的table布局
  • 固定头
  • 固定列

三、实现的效果

以及组件的使用方式期望是这样:

const column = [{ name: '序号', key: 'order', width: '100px', isFixed: true },{ name: '第二列', key: 'col2', width: '200px' },{ name: '第三列', key: 'col3', width: '300px' },{ name: '第四列', key: 'col4', width: '200px' },{ name: '第五列', key: 'col5', width: '500px' },{ name: '第六列', key: 'col6', width: '200px' },
];
const data = [{ order: 1, col2: '1-2', col3: '1-3', col4: '1-4', col5: '1-5', col6: '1-6' },{ order: 2, col2: '2-2', col3: '2-3', col4: '2-4', col5: '2-5', col6: '2-6' },{ order: 3, col2: '3-2', col3: '3-3', col4: '3-4', col5: '3-5', col6: '3-6' },{ order: 4, col2: '4-2', col3: '4-3', col4: '4-4', col5: '4-5', col6: '4-6' },{ order: 5, col2: '5-2', col3: '5-3', col4: '5-4', col5: '5-5', col6: '5-6' }
];
<CustomTablecolumn = { column } data = { data } rowHeight = { '100px' } /> 

四、动手实现

4.1、table组件的尺寸

4.1.1、如何定义table组件的宽?

对于table组件来说,定义宽度有2种方式,一种是"固定宽度布局",另一种是"自由宽度布局"。我们可以通过"table-layout"属性在这2种方式中做出选择。

4.1.1.1、“固定宽度布局”

就是显示的定义table标签的宽度(即table-layout: fixed),那么此时 <table> 的宽度 与 各列 [td | th] 的宽度的关系如下:

  • table真实的width = Math.max(table.style.width, 各列宽度之和)
  • 如果 table.style.width > 各列宽度之和,那么二者之差再除以列数得到的结果将增加到各列上。

纸上得来终觉浅,我们来跑下代码加深一下上述节论的记忆(以下均是react环境下的代码):

table {table-layout: fixed;border-collapse: collapse;border-spacing: 0px;width: 200px;
} 
<div className = 'yon-table-box'><table><colgroup><col style={{ width: '100px' }}><col style={{ width: '100px' }}><col style={{ width: '100px' }}><colgroup><thead><th>列1</th><th>列2</th><th>列3</th></thead></table>
</div> 

此时效果如下:

此时我们再稍微改动下样式,css如下:

table {table-layout: fixed;width: 500px;border-collapse: collspse;border-spacing: 0px;
} 

效果如下:

此时我们发现,上述结论是对的。

4.1.1.2、“自由宽度布局”

顾名思义,更容易实现这个等式 “table.style.width == 各列宽度之和 + 边框 + 间距”。通过设置 table-layout: auto 来触发。

我们来跑下代码试一下:

table {table-layout: auto;border-collapse: collapse;border-spacing: 0px;width: auto;
} 
<div className = 'table-box'><table><colgroup><col style={{ width: '100px' }}><col style={{ width: '100px' }}><col style={{ width: '100px' }}></colgroup><thead><th>列1</th><th>列2</th><th>列3</th></thead></table>
</div> 

此时我们来看下效果:

4.1.1.3、table宽度的最终选择

经过上面2种布局的分析,我们决定选用"自由宽度布局"的方式定义table的宽度,此时咱们的CustomTable组件的代码如下:

 .yon-table-box {width: 100px;height: 100px;overflow: scroll;}table {table-layout: auto;table-collapse: collapse;table-spacing: 0px;width: auto;}th {background: #f4f4f4;} 
 import React from 'react';class CustomTable extends React.Component {constructor(props){super(props)}render (){const { column } = this.props;return <div className = 'yon-table-box'><div className = 'right'>
                    <table>
                        <colgroup>{column.map(colItem => {return <col style={{ colItem.width }}>})}</colgroup><thead><tr>{column.map(colItem => {return <th>{ colItem.name }</th>})}</tr></thead>
                    </table>
                </div></div>}} 

Table的使用:

 import React from 'react';const column = [{ name: '列1', key: 'col1', width: '100px' },{ name: '列2', key: 'col2', width: '200px' },{ name: '列3', key: 'col3', width: '300px' },];<CustomTable column = { column } /> 

4.1.2、如何定义table组件的高?

定义table组件的行高度,这个就比较简单了,我们只需要保证 table.style.height = ‘auto’, 即可实现自定义行高,CustomTable组件的代码如下:

 .yon-table-box {width: 100px;height: 100px;overflow: scroll;}table {table-layout: auto;table-collapse: collapse;table-spacing: 0px;width: auto;// 新增代码+++++++++++++++++++++height: auto;}th {background: #f4f4f4;}// 新增代码++++++++++++++++++++++++++td {background: #fff;border-bottom: 1px solid rgb(208, 208, 208);} 
 import React from 'react';class CustomTable extends React.Component {constructor(props){super(props)}render (){const { column, data, rowHeight } = this.props;return <div className = 'yon-table-box'><div className = 'right'>
                    <table>
                        <colgroup>{column.map(colItem => {return <col style={{ colItem.width }}>})}</colgroup><thead><tr>{column.map(colItem => {return <th>{ colItem.name }</th>})}</tr></thead><tbody>{// 新增代码+++++++++++++++++++++++++data.map(dataItem => {return <tr>{column.map(colItem => {return <td style={{ height: rowHeight }}>{dataItem[colItem.key]}</td>})}</tr>})}</tbody>
                    </table>
                </div></div>}} 

好啦,此时咱们的第一个功能到这就算实现啦,如果你是一步一步跟下来的,那么此时的效果应该是这样的:

4.2、固定列

我们先来看一下下面的图:

首先,根据目前实现的效果来看,我们可以得出以下信息:

  • 目前dom上就只有一个table,意味着 表头、表体是连着的,符合正常文档流所有的特点。
  • 红色框是我们想要固定的列。

好,此时我们来想一下能够做到固定列(无非是2个div互不干扰呗)的方法有哪些?

4.2.1、方式一

把一个table分为左右2个table,并保证只有右侧的table局部滚动,干掉父div的scroll。

4.2.2、方式二

依旧是一个table分为左右2个table,只是在不改变现在滚动的情况下,给左侧的table新增一个"sticky"的粘性定位即可。

4.2.3、方式三

利用js,监听scroll事件,在滚动的时候,利用transform属性,动态的设置translateX,来达到固定列的效果

4.2.3、固定列的最终选择

这里我们选择 方式二。理由非常简单,改动量非常少的情况就可以实现,其他的2种情况的改动都挺大,但是也可以实现,下面我会把其他的2种方式的伪代码写出来供大家参考。

  • 方式一。其实这种方式更适合"固定宽度布局"的table,因为这种table的width是非常容易拿到的,你只需要保证列数过多的时候出现滚动条就可以(也仅仅是一行样式的问题),伪代码如下:
 import React from 'react';export default class App extends React.Component {constructor(props){super(props)}render (){return <div style={{ display: 'flex' }}><!-- 左侧固定列 --><div className = 'left'><table></table></div><!-- 右侧自由列 --><div className = 'right' style={{ overflow: 'scroll' }}><table></table></div></div>}} 
  • 方式三。这种利用js的方式是最看重代码结构的,而且还会使你的代码变得非常臃肿(不利于拆分),它比较适用于下面的写法:
 import React from 'react';// 大家看到这样的写法其实可能感觉不到什么,但是如果这样写的话,table组件的抽取将会异常困难。class App extends React.Component {constructor(props){super(props)this.leftTableRef = React.createRef();this.scrollTableBox = React.createRef();}scrollTable = () => {// 保持左侧固定不动this.leftTableRef.current.style.transform = `translateX(-${this.scrollTableBox.current.scrollLeft}`;}render (){return <div ref = { this.scrollTableBox } style={{ display: 'flex', overflow: 'scroll' }} onScroll = { () => this.scrollTable() }><!-- 左侧固定列table --><table ref={ this.leftTableRef }></table><!-- 右侧自由列table --><table></table></div>}} 

4.2.4、固定列的最终代码

 .yon-table-box {width: 100px;height: 100px;overflow: scroll;}table {table-layout: auto;table-collapse: collapse;table-spacing: 0px;width: auto;// 新增代码+++++++++++++++++++++height: auto;} th { background: #f4f4f4;} // 新增代码++++++++++++++++++++++++++td {background: #fff;border-bottom: 1px solid rgb(208, 208, 208);}// 新增代码++++++++++++++++++++++++++.left {position: sticky;left: 0px;} 
 import React from 'react';class CustomTable extends React.Component {constructor(props){super(props)this.state = {// 固定列集合fixedColumnArr : [];// 自由列集合notFixedColumnArr: [];}}componentDidMount(){const { column } = this.props;this.setState(state => {return {// column配置项中,isFixed用于标识是否是固定列fixedColumnArr : column.filter(item => item.isFixed),notFixedColumnArr: column.filter(item => !item.isFixed)}})}render (){const { data, column, rowHeight } = this.props;const { fixedColumnArr, notFixedColumnArr } = this.state;return <div className = 'yon-table-box'>{ fixedColumnArr.length > 0&& <div className = 'left'> <table> <colgroup> { fixedColumnArr.map(item => { return <col style={{ minWidth: item.width, width: item.width }}> }) } </colgroup> <thead> <tr> { fixedColumnArr.map(item => { return <th>{item.name}</th> }) } </tr> </thead> <tbody> { data.map(dataItem => { return <tr> { fixedColumnArr.map(colItem => { return <td>{dataItem[colItem.key]}</td> }) } </tr> }) } </tbody> </table> </div>}<div className = 'right'><!-- 跟left代码一样,只需要把 fixedColumnArr 换成 notFixedColumnArr 即可 --></div></div>}} 

4.3、固定头

固定头的实现思路 其实和 固定列的思路是一样的,这里就不具体分析了,夜也深了,哈哈哈,这里我就偷点懒,给伪代码了:

 import React from 'react';class CustomTable extends React.Component {constructor(props){super(props)this.state = {// 固定列集合fixedColumnArr : [];// 自由列集合notFixedColumnArr: [];}}render (){const { data, column, rowHeight } = this.props;return <div className = 'yon-table-box'>// 整张表的头部(整个头部是需要sticky的)<div className = 'top'><div className = 'left'></div><div className = 'right'></div></div>// 整张表的body(左侧需要固定的body是需要sticky的)<div className = 'bottom'><div className = 'left'></div><div className = 'right'></div></div></div>}} 

五、最后

好啦,table组件这次就分享到这里了,文章里有讲的不对的地方,欢迎大家指正,如果大家对table组件的实现有什么好的想法,也欢迎评论留言,那么886~~~

最后

为大家准备了一个前端资料包。包含54本,2.57G的前端相关电子书,《前端面试宝典(附答案和解析)》,难点、重点知识视频教程(全套)。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/361600.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot整合Spring Security过滤器链加载执行流程源码分析

文章目录1.引言2.Spring Security过滤器链加载1.2.注册名为 springSecurityFilterChain的过滤器2、查看 DelegatingFilterProxy类3.查看 FilterChainProxy类3.1 查看 doFilterInternal方法。3.2 查看 getFilters方法。4 查看 SecurityFilterChain接口5 查看 SpringBootWebSecur…

JDK8增加的特性

Java知识点总结&#xff1a;想看的可以从这里进入 目录13、JDK8增加的特性13.1、Lambda表达式13.2、方法的引用13.3、时间处理类13.4、接口增加方法13.5、注解新增13.6、Optional类13.7、Stream13、JDK8增加的特性 13.1、Lambda表达式 Lambda表达式和方法的引用 13.2、方法的…

Java8的Optional类的使用 和 Stream流式操作

Java知识点总结&#xff1a;想看的可以从这里进入 目录13.6、Optional类13.7、Stream13.7.1、Stream创建13.7.2、中间操作1、筛选2、切片3、映射4、排序13.7.3、终止操作1、遍历2、聚合3、匹配查找4、归约5、收集归集统计分组字符串拼接归约13.6、Optional类 Optional类是一个…

【已解决】异常断电文件损坏clickhouse启动不了:filesystem error Structure needs cleaning

问题 办公室有一台二手服务器&#xff0c;作为平时开发测试使用。由于机器没放在机房&#xff0c;会偶发断电异常断电后&#xff0c;文件系统是有出问题的可能的&#xff0c;尤其是一些不断在读写合并的文件春节后&#xff0c;发现clickhouse启动不了&#xff0c;使用systemct…

【Nginx】【一】Nginx简介

Nginx简介 背景介绍 Nginx&#xff08;“engine x”&#xff09;一个具有高性能的【HTTP】和【反向代理】的【WEB服务器】&#xff0c;同时也是一个【POP3/SMTP/IMAP代理服务器】&#xff0c;是由伊戈尔赛索耶夫(俄罗斯人)使用C语言编写的&#xff0c;Nginx的第一个版本是200…

nginx的平滑升级、反向代理负载均衡

文章目录一、负载均衡介绍二、nginx的平滑升级和版本回滚1.平滑升级2.版本回滚3.本实验纯代码过程三、反向代理负载均衡总结一、负载均衡介绍 四层负载均衡 所谓四层负载均衡是指OSI七层模型中的传输层, 那么传输层Nginx已经支持TCP/IP的控制, 所以只需要对客户端的请求进行TCP…

RPC编程:RPC概述和架构演变

RPC编程系列文章第一篇一&#xff1a;引言1&#xff1a;本系列文章的目标2&#xff1a;RPC的概念二&#xff1a;架构的演变过程1&#xff1a;单体架构1)&#xff1a;概念2)&#xff1a;特点3)&#xff1a;优缺点2&#xff1a;单体架构水平扩展1)&#xff1a;水平拓展的含义2)&a…

提到数字化,你想到哪些关键词

我们的生活中已经充满了数据&#xff0c;各种岗位例如运营、市场、营销上也都喜欢在职位要求加上一条利用数据、亦或是懂得数据分析。事实上&#xff0c;数据已经成为了构建现代社会的基本生产要素&#xff0c;并且因为不受自然环境的限制&#xff0c;已经成为了人们对未来社会…

【论文笔记】Manhattan-SDF==ZJU==CVPR‘2022 Oral

Neural 3D Scene Reconstruction with the Manhattan-world Assumption 本文工作&#xff1a;基于曼哈顿世界假设&#xff0c;重建室内场景三维模型。 1.1 曼哈顿世界假设 参考阅读文献&#xff1a;Structure-SLAM: Low-Drift Monocular SLAM in Indoor EnvironmentsIEEE IR…

ASEMI高压MOS管ASE20N65SE体积,ASE20N65SE大小

编辑-Z ASEMI高压MOS管ASE20N65SE参数&#xff1a; 型号&#xff1a;ASE20N65SE 漏极-源极电压&#xff08;VDS&#xff09;&#xff1a;650V 栅源电压&#xff08;VGS&#xff09;&#xff1a;30V 漏极电流&#xff08;ID&#xff09;&#xff1a;20A 功耗&#xff08;P…

Ardiuno-交通灯

LED交通灯实验实验器件&#xff1a;■ 红色LED灯&#xff1a;1 个■ 黄色LED灯&#xff1a;1 个■ 绿色LED灯&#xff1a;1 个■ 220欧电阻&#xff1a;3 个■ 面包板&#xff1a;1 个■ 多彩杜邦线&#xff1a;若干实验连线1.将3个发光二极管插入面包板&#xff0c;2.用杜邦线…

Docker----------day-mysql8主从复制

1.安装master 1.1拉取镜像 docker search mysql docker pull mysql mkdir -p /home/mysql8/data mkdir -p /home/mysql8/conf mkdir -p /home/mysql8/log1.2 2.启动交互式添加容器数据卷 #不添加容器数据卷 docker run -p 3307:3306 --name mysql_master -e MYSQL_ROOT_PASS…

xss基础

目录标题一、XSS的原理二、XSS漏洞分类1、反射型xss2、存储型XSS3、基于DOM的XSS三、XSS漏洞的危害及验证四、XSS漏洞的黑盒测试五、XSS漏洞的白盒测试一、XSS的原理 跨站脚本攻击XSS&#xff08;Cross Site Scripting&#xff09;&#xff0c;为了不和层叠样式表&#xff08;…

有序表之红黑树

文章目录1、五个条件2、调整策略2.1 插入调整的情况2.1.1 情况一&#xff1a;插入节点是红色&#xff0c;其父节点也是红色2.1.2 情况二2.1.2 代码实现2.2 删除调整的情况2.2.1 情况一&#xff1a;双重黑节点的兄弟节点也是黑色&#xff0c;且其兄弟的两个孩子也是黑色2.2.2 情…

100亿级订单怎么调度,来一个大厂的极品方案

背景 超时处理&#xff0c;是一个很有技术难度的问题。 所以很多的小伙伴&#xff0c;在写简历的时候&#xff0c;喜欢把这个技术难题写在简历里边&#xff0c; 体现自己高超的技术水平。 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;尼恩经常指导大家 优化简历。 最…

教你学git

前言 git是一种用于多人合作写项目。详细说明如下 文章目录前言什么是版本控制&#xff1f;什么是 Git&#xff1f;它就属于人工版本控制器版本控制工具常见版本控制工具怎么工作的&#xff1f;git 文件生命周期状态区域安装配置-- global检查配置创建仓库工作流与基本操作查看…

高精密数字源表的发展史

在半导体、汽车、医疗等高端制造行业&#xff0c;源表通常被用于半导体材料或精密器件的电性能特性测试和生产测试应用&#xff0c;以及中低电平测试和实验室研究使用。源表采用四象限工作模式&#xff0c;可以在提供精密电压、电流源的同时&#xff0c;又能够作为电压、电流、…

浅谈Java线程池中的ThreadPoolExecutor工具类

目录 ThreadPoolExecutor的构造函数 关于线程池的一些补充 线程池运行原理分析 概念原理解释 整个流程图如下&#xff1a; 一点补充 创建线程池主要有两种方式&#xff1a; 通过Executor工厂类创建&#xff0c;创建方式比较简单&#xff0c;但是定制能力有限通过ThreadPoo…

Git ---- 概述

Git ---- 概述1. 何为版本控制2. 为什么需要版本控制3. 版本控制的工具集中式版本控制工具分布式版本控制工具4. Git 简史5. Git 工作机制6. Git 和代码托管中心Git 是一个免费的、开源的分布式版本控制系统&#xff0c;可以快速高效地处理从小型到大型的各种项目。 Git 易于学…

深入浅出C++ ——继承

文章目录一、继承的相关概念1. 继承的概念2. 继承格式3. 继承方式4. 访问限定符5. 继承基类成员访问方式的变化二、基类和派生类对象赋值转换三、继承中的作用域四、派生类的默认成员函数五、继承与友元六、继承与静态成员七、菱形继承及菱形虚拟继承1. 单继承2. 多继承3. 菱形…