[前端/JavaScript] 实作汇出excel下载按钮的超好用套件:ExcelJS(下)- 用React汇出excel (export excel)

有关於ExcelJS这个套件的教学与说明,请先看我的上一篇文章:
[前端/ES6] 实作汇出excel下载按钮的超好用套件:ExcelJS(上)- 基础介绍

这一篇呢,我们着重於在react中使用这个套件
以及要来发挥我们react的优点,把这个按钮包装成一个通用的 UI component

会特别写这篇的原因也主要是我自己一开始在查 「react export excel」、「react 汇出excel」
找到的文件我觉得不够详尽,而且没有一个现成的、包装好的UI可以使用
好啦,废话不多说,先来做一个最基础的范例吧!

React + ExcelJS

让我们把上次的onClick function和react直接做一个简单的结合如下

  • react + 事件定义 (js版本)
    线上 codesandbox Demo: 简易react + onClick范例

    import React from "react";
    import ExcelJs from "exceljs";
    
    function ExportExcelButton (){
      function onClick(){
        const workbook = new ExcelJs.Workbook(); // 创建试算表档案
        const sheet = workbook.addWorksheet('工作表范例1'); //在档案中新增工作表 参数放自订名称
    
    		sheet.addTable({ // 在工作表里面指定位置、格式并用columsn与rows属性填写内容
    	    name: 'table名称',  // 表格内看不到的,算是key值,让你之後想要针对这个table去做额外设定的时候,可以指定到这个table
    	    ref: 'A1', // 从A1开始
    	    columns: [{name:'名字'},{name:'年龄'},{name:'电话'}],
    	    rows: [['小明','20','0987654321'],['小美','23','0912345678']]
    		});
    
        // 表格里面的资料都填写完成之後,订出下载的callback function
    		// 异步的等待他处理完之後,创建url与连结,触发下载
    	  workbook.xlsx.writeBuffer().then((content) => {
    		const link = document.createElement("a");
    	    const blobData = new Blob([content], {
    	      type: "application/vnd.ms-excel;charset=utf-8;"
    	    });
    	    link.download = '测试的试算表.xlsx';
    	    link.href = URL.createObjectURL(blobData);
    	    link.click();
    	  });
    	}
      return (
          <button onClick={onClick}> 下载excel </button>
        )
    };
    

    你会发现其实这个就是把那些上次的function直接复制过来
    然後render 一个button ,按下後会触发

    一样是按下之後,就会得到这样的档案

    成品

包装成一个通用的 Component

  • TS版本
    (markdown好像没有支援TSX所以我只能先选typescript, 最後面return元件的语法颜色有点跑掉)

    import React, { CSSProperties } from "react";
    import ExcelJs from "exceljs";
    
    export type SheetData = {
    	sheetName: string,  // 工作表名称
        thead: Array<string>,  // 栏位标题,例如:['姓名','年龄','电话']
        tbody: Array<Array<string>>, 
        // 内容,例如:[['小明','20','0987654321'],['小美','23','0912345678']]
        columnWidths?: Array<{number: number, width: number}> //用来指定栏宽的
    }
    
    interface ExportExcelButtonProps {
        fileName: string, // 档案名称
        sheetDatas:Array<SheetData> , // 要汇出的表格资料 
        disabled?: boolean, // 是不是要禁止按钮动作
        buttonRef?: React.MutableRefObject<any>, // 外面用useRef传进来
        style?: CSSProperties // 按钮的style
    }
    
    export function ExportExcelButton (props: ExportExcelButtonProps){
      function onClick(){
        const workbook = new ExcelJs.Workbook();
        props.sheetDatas.forEach((sheetData: SheetData)=>{
            const sheet = workbook.addWorksheet(sheetData.sheetName);
            sheet.addTable({
                name: sheetData.sheetName,
                ref: `A1`, // 从A1开始
                headerRow: true,
                columns: sheetData.thead.map((s)=>{ return {name: s}}),
                rows: sheetData.tbody
            });
            if (sheetData.columnWidths) {
              sheetData.columnWidths.forEach((column)=>{
                  sheet.getColumn(column.number).width = column.width
              });
            }
        })
    
        // 表格里面的资料都填写完成之後,订出下载的callback function
    		// 异步的等待他处理完之後,创建url与连结,触发下载
        workbook.xlsx.writeBuffer().then((content: ExcelJs.Buffer) => {
          const link = document.createElement("a");
          const blob = new Blob([content], {
            type: "application/vnd.ms-excel;charset=utf-8;"
          });
          link.download = `${props.fileName}.xlsx`;
          link.href = URL.createObjectURL(blob);
          link.click();
        });
      };
    
      const style: CSSProperties = {
          borderRadius: '5px',
          ...props.style
      }
    
      return (
          <button
              ref={props.buttonRef}
              disabled={props.disabled}
              onClick={onClick}
              style={style}
          >
            汇出excel
          </button>
        )
    };
    
    export default ExportExcelButton;
    
  • JS版本

    import React from "react";
    import ExcelJs from "exceljs";
    
    export function ExportExcelButton (props){
      function onClick(){
        const workbook = new ExcelJs.Workbook();
        props.sheetDatas.forEach((sheetData)=>{
            const sheet = workbook.addWorksheet(sheetData.sheetName);
            sheet.addTable({
                name: sheetData.sheetName,
                ref: `A1`, // 从A1开始
                headerRow: true,
                columns: sheetData.thead.map((s)=>{ return {name: s}}),
                rows: sheetData.tbody
            });
            if (sheetData.columnWidths) {
              sheetData.columnWidths.forEach((column)=>{
                  sheet.getColumn(column.number).width = column.width
              });
            }
        })
    
        // 表格里面的资料都填写完成之後,订出下载的callback function
    		// 异步的等待他处理完之後,创建url与连结,触发下载
        workbook.xlsx.writeBuffer().then((content) => {
          const link = document.createElement("a");
          const blob = new Blob([content], {
            type: "application/vnd.ms-excel;charset=utf-8;"
          });
          link.download = `${props.fileName}.xlsx`;
          link.href = URL.createObjectURL(blob);
          link.click();
        });
      };
    
      const style = {
          borderRadius: '5px',
          ...props.style
      }
    
      return (
          <button
              ref={props.buttonRef}
              disabled={props.disabled}
              onClick={onClick}
              style={style}
          >
            汇出excel
          </button>
        )
    };
    
    export default ExportExcelButton;
    
  • 使用范例

    import ExportExcelButton from "./ExportExcelButton";
    
    function App(){
    	const downloadData = [
            { 
              sheetName: `工作表1`,
    	      thead: ['姓名','年龄','电话'],
    	      tbody: [['小明','20','0987654321'],['小美','23','0912345678']],
    	      columnWidths: [{number: 1, width:20},{number: 2, width:10},{number: 3, width:40}]
    		},
            { 
              sheetName: `工作表2`,
    	      thead: ['姓名','座号'],
    	      tbody: [['小明','1'],['小美','2']],
    	      columnWidths: [{number: 1, width:20}]
    		}
    	];
    
    	return (
    		<ExportExcelButton
    		  fileName={'测试的试算表'}
    		  sheetDatas={downloadData}
    		/>
    	)
    }
    
    

    如此一来,就可以将JSON格式的资料,转成excel下载
    而且对component外来说,只要整理好格式,试算表里面就能够有好几张工作表

    这边也一样提供以上的完整范例
    线上codesandbox demo: TS componentJS component

    也欢迎大家根据这个示范,去准备自己的UI库

    踩到的坑

    我个人是使用react搭配 nx 这套toolchain 来进行开发的
    题外话提一下nx这套toolchain
    他有好用的环境建置,能做应用整合、共用LIB、方便搭配jest、e2e等优点
    创建应用之後会有设置production这个configurations,帮忙分出测试的打包配置与正式发布的配置
    但事情就这麽发生了。

    • 错误讯息:webpack.WebpackError is not a constructor

      使用环境: 使用nx 这套toolchain 建置react专案

      问题发生: import 这个套件之後,就发现build专案时,只要选择production 配置
      就会执行失败,并喷出错误讯息: webpack.WebpackError is not a constructor

    解决方式:
    主要是引入该套件之後,会产生大量warning
    而预设设定在webpack.json中的production参数如下
    https://ithelp.ithome.com.tw/upload/images/20220324/201357504JaarSBcZk.png
    注意到budgets的部分,里面的maximumWarning 设置了最大warning为2mb
    所以只要把这个数值拉高就可以了 (个人把他直接拉高到10mb)

    等等,就这样?

    对。就这样。我从错误讯息中根本看不出来是喷很多警告的关系,所以他花了我一小时在那边爬文
    如果各位引入套件之後,有类似的情形,可以先找找你家的webpack.json
    把警告和错误讯息的上限拉高看看

结语

exceljs这个套件就介绍到这边
本来有想要示范怎麽用react做一个可以异步取得资料的按钮
不过想想感觉已经跟这个套件本身脱离关系了
比较像是迁就他 触发 => 建立表格、塞资料、触发下载
这个机制所必须做的行为,跟..react 还有promise比较有关

如果有人有留言敲碗的话,我再写一篇好了
就这样!希望文章都有帮助到大家~让大家少踩一点坑
还有让exceljs的文章多一点繁体字的介绍...

喜欢这篇文的话可以帮我点一个喜欢~ 我会很开心XDD (真是虚荣的女人

以上!


<<:  [前端/JavaScript] 实作汇出excel下载按钮的超好用套件:ExcelJS(上)- 基础介绍与教学

>>:  18. STM32-CAN-BUS (上)

使用 KubeEye 为你的 K8s 集群安全保驾护航

使用 KubeEye 为你的 K8s 集群安全保驾护航 其他 2022-04-24 18:51:30...

资安学习路上-学习资源整理

学习资源整理-资安社团 决定好要学习哪个面向,那要去哪里学呢??是不是要花大钱去补习才能学得会呢? ...

Material UI in React [ Day 8 ] Inputs(Checkbox) 多选

现在就让我们回到Inputs的部分继续讲解,今天会讲解表单中常用的多选(checkbox)的作法。 ...

Day 3 - 新人报到前的准备与莫名的焦虑感

确定了offer也确定了报到时间後,距离到职日大概还有两周多的时间,因为自己是北漂青年因此开始寻找後...

7. 如何写好document

前言 (因为最近时间有点少XD,所以偷偷选了比较短的影片来硬塞XD) 这篇显然就是个蛮一般的top...