人的本事靠自己,人的发展靠网络
各人好,我是柒八九。一个专注于前端开发技能/Rust及AI应用知识分享的Coder
此篇文章所涉及到的技能有
- Rust( Rust吸收json对象并剖析/Rust天生xml)
- WebAssembly
- 表格归并(静态/动态)
- React/Vue表格导出 excel
- Rspack
由于,行笔墨数所限,有些概念大概会一带而过亦大概提供对应的学习资料。请各人酌情观看。
<hr> 媒介
自从前次更文Rust赋能前端: 给我0.02秒,天生一套Vite/Rsbuild前端项目已已往半个月之久了。
不是偷懒和怠惰了。而是年底了,工作有点多。以是,导致更文的速率和频率有点降落。
想必各人在平常业务开发的时间,或多或少都有过将前端页面中的table导出excel的需求。
通例的方案有几种
- 纯后端处理处罚,也就是发起一个异步任务,然后将 excel天生移步到后端。
- 优点:这种环境,针对那种 数据量大的环境,是一种可选方案。如果数据过于巨大,我们还可以在用一个 导出页面来展示各种导出任务。(已导出/正在导出…)
- 缺点:我们无法做到导出任务的时效性。固然,我们可以借助 websockt/sse等方案来吸收后端的导出结果。但是呢,这种方式无疑增长业务的复杂度。
- 纯前端处理处罚,我们可以借助一些第三方的库比方 SheetJS [1]来实行数据的导出。
- 优点:导出结果可以或许实时看到。
- 缺点:处理处罚数据量大的表格,性能就有点慢。同时,比如做一些表格归并(静态/动态)就有点贫苦,然后如果我们想对导出的 excel某些 cell做样式处理处罚,这块也有很大的上手难度。
而,本日呢,我们提供一种方案,用Rust来处理处罚前端表格的导出(excel)。末了的结果就是,我们既可以实时得到导出结果,也能针对大数据表格实现高性能导出。而且还可以实现表格归并(静态/动态)。
运行结果
静态表格
静态长表格(1万条数据)
静态表格归并
动态表格归并
<hr> 好了,天不早了,干点正事哇。
<hr> 我们能所学到的知识点
<hr> 1. 初志
着实呢,我们公司对于前端表格的导出,是走的纯后端处理处罚的模式。也就是
- 在前端页面中发起一个异步任务
- 后端将特定的数据添补到 excel中
- 后端向前端在返回一个 Blob对象
export?const?exportxxRecord?=?(data)?=>?{ ??return?axiosInstance<Blob>({ ????url:?`xxx`, ????data, ????responseType:?'blob', ????method:?'POST', ??}); };
- 前端天生一个 a标签来实行下载任务
export?const?downLoad?=?(blob:?Blob,?fileName:?string)?=>?{ ??const?url?=?window.URL.createObjectURL(blob); ??const?a?=?document.createElement('a'); ??a.href?=?url; ??a.download?=?fileName; ??a.click(); ??window.URL.revokeObjectURL(url); };
但是呢,迩来接到一个需求。这个需求可谓是Buff叠满。
- 表格列( columns)是动态天生的
- 表格数据也是动态的(非静态表格)
- 表格数据中特定列的数据须要实行归并处理处罚,而且列和列之前是有包罗关系的
- last but not least,表格导出的 excel也是须要举行列归并的
然后,更诡异的是,后端同砚说他实现不了excel的动态归并。 这你能受的了吗。
既然,锅已经甩过来了,那没有不接的原理。正所谓,我不入地狱,谁入地狱?。那还是由我来哇。
更深的逻辑
着实,大部分业务场景中,各人对导出Table为Excel的通例做法都是通过异步接口来实现的。如许做也是有肯定的利益的。对于部分业务场景,我们须要记任命户的导出记录,这个利用就须要后端将记录入库。
但是呢,对于一些非后端记录的导出,我们就可以利用纯前端的方式了。着实针对这类的业务处理处罚,是有很多利益的。
- 针对导出,无非就是将前端页面中展示的 Table导出为 Excel。此时,在前端环境中,我们在利用 Antd/Element等前端组件库展示的时间,这块导出数据前端已经知晓了。我们要做的就是对于这些数据再次拼装大概直接一股脑的扔给 Excel导出引擎(我们就是采取这种方式)
- 通过异步方式处理处罚,无论数据多少,都会产生 网络时延。如果在弱网环境下,就算是数据量小的环境下,导出结果也不尽人意。我们在 22023口试真题之网络篇中讲过。
最好最快的哀求就是没有哀求
- 就算网络时延不是重要的性能消耗点,但是对于一些统计范例的表格,对于后端同砚是须要举行 多表关联的查询。有些看似简朴的数据值,大概须要凌驾很多表去查询。这也是一个性能消耗点。
以是,如果上天给我一种可以或许在前端环境中,又快又好的导出excel。我会绝不夷由的利用它
<hr> 2. 案例展示
写在最前面
由于,我们是先讲我们wasm的本领,背面才会涉及到源码部分。但是呢,由于我们这个wasm兼容了很多环境,以是参数也是有很多传人方式和格式。以是,我们在讲示例之前,先讲讲参数的寄义。
我们在Rust中界说了和参数相干的Struct用于网络相干信息。
#[derive(Deserialize)] pub?struct?CellCoords?{ ????pub?column:?u32, ????pub?row:?u32, } #[derive(Deserialize)] pub?struct?MergedCell?{ ????pub?from:?CellCoords, ????pub?to:?CellCoords, } #[derive(Debug,?Serialize,?Deserialize)] struct?Column?{ ????title:?String, ????width:?serde_json::Value, ????dataIndex:?String, } #[derive(Deserialize)] struct?InputJson?{ ????name:?String, ????columns:?Vec<Column>, ????source:?Vec<std::collections::HashMap<String,?serde_json::Value>>, ????merge:?Option<Vec<MergedCell>>, ????correlation:?Option<Vec<String>>, }
- name:吸收一个 String范例的数据,用于设置天生 excel的 sheetName。
- columns:看 Struct我们得知,它吸收的是 Column的数组,而 Column是用于界说我们每列的详细信息。
- 可以看到,类似 title/width/dataIndex都是我们在前端构建 Table( Antd)中用到的字段。(固然,当利用 Element时,你可以将对应的结构转换成此种范例)
- 着实这里有一个告诫,在 Rust中我们界说变量名,都是利用 **蛇形定名法(snake_case)**是指每个空格皆以底线( _)取代的誊写风格,且每个单字的第一个字母皆为 小写。 但是,我们为最大水平的兼容前端的数据,不须要再转换,这里就利用了驼峰定名法
- 固然,我们也不须要在传人的时间,在前端处理处罚 columns相干字段,无脑传即可
- 这里尚有一点须要分析,在前端 columns我们界说 width时间,是可以吸收 number和 string范例,在 Rust中我们利用 serde_json::Value来界说范例
- source:这里我们用 Vec<std::collections::HashMap<String, serde_json::Value>>界说,对标前端数据的数据范例就是 对象。
- 也就是说,我们 source传人对象即可,也是无脑传即可
- merge: 是一个可选项,用于吸收静态表格归并的信息
- 此中 MergedCell吸收 from和 to的相干信息。
- correlation:这也是一个可选项,用于吸收对于针对 列归并时对应列的 dataIndex信息。
- 如果传人多个字段,那么这些字段默认有 关联关系,背面的字段会从前面字段分组后,才会实行归并利用。
关于在Rust中怎样利用JSON相干的,可以看我们之前写的如安在Rust中利用JSON
<hr> 项目初始化
还是熟悉的套路,我们利用npx f_cli_f create table2excel的前端项目。(发布到npm的f_cli_f的rspack版本的.gitignore缺失了,有空我重新发布一版)
我们选择rspack+antd+react+tailwind的前端模板。(下面的方案,着实和框架无关,也就是说我们可以在React/Vue中无痛利用该方案)
然后,我们将项目中的pages/Home中的更换为下面的代码。
import?init,?{?generate_excel?}?from?"@/wasm/table2excel"; import?{?Button,?Table?}?from?"antd"; import?{?useCallback,?useEffect,?useState?}?from?"react"; import?{?mergeDynamicTable,?mergeTable,?staticTable?}?from?"./data.js"; const?Home?=?()?=>?{ ?const?[time,?setTime]?=?useState(0); ?useEffect(()?=>?{ ??const?initWasmInstance?=?async?()?=>?{ ???await?init(); ??}; ??initWasmInstance(); ?},?[]); const?handleExcelBlob?=?(res:?Blob)?=>?{ ????const?blob?=?new?Blob([res],?{ ????????type:?"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,", ????}); ????const?a?=?document.createElement("a"); ????a.href?=?URL.createObjectURL(blob); ????a.download?=?"data.xlsx"; ????document.body.append(a); ????a.click(); }; const?handleExport4Static?=?async?()?=>?{ ????const?startTime?=?performance.now(); ????const?res?=?await?generate_excel({ ????????columns:?staticTable.columns, ????????source:?staticTable.source, ????????name:?"front789", ????????merge:?[], ????}); ????const?endTime?=?performance.now(); ????const?duration?=?endTime?-?startTime;? ????setTime(duration);? ????handleExcelBlob(res); }; ?return?( ??<section?className="h-screen?w-screen?overflow-auto?flex?flex-col?gap-10?p-20"> ???<div?className="flex?flex-col?gap-2"> ????<div?className="flex?items-center?gap-5"> ?????静态表格?<Button?onClick={handleExport4Static}>导出</Button>?<span>耗时:{time}ms</span> ????</div> ????<Table ?????columns={staticTable.columns} ?????bordered ?????dataSource={staticTable.source} ?????pagination={false} ????/> ???</div> ??</section> ?); }; export?default?Home;
此中,有几个外部文件,我们简朴说形貌一下
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |