前言
- 常网IT戳我呀!
- 常网IT源码上线啦!
- 本篇录入技术选型专栏,希望能祝君拿下Offer一臂之力,各位看官感兴趣可移步🚶。
- 有人说面试造火箭,进去拧螺丝;其实个人觉得问的问题是项目中涉及的点 || 热门的技术栈都是很好的面试体验,不要是旁门左道冷门的知识,实际上并不会用到的。
- 接下来想分享一些自己在项目中遇到的导出功能场景。
如果你觉得你现在做的事情很累,很难,那一定是你方法用错了。
一、问题剖析
那是一个倾盆大雨的早上,花瓣随风雨落在我的肩膀上,是五颜六色的花朵。
我轻轻抚摸着他,随后拨开第一朵花瓣,她不爱我。
拨开第二朵,她爱我。
正当我沉迷于甜蜜的幻想中,后端小白🙋喊道:这个导出你前端应该就能做的吧!
🙋🏻♂️那是自然,有什么功能是我大前端做不了的,必须得让你们大开眼界。
二、为什么导出要前端做?
前端导出的场景:
- 轻量级数据:如果要导出的表格数据相对较小,可以直接在前端生成和导出,避免服务器端的处理和通信开销。
- 数据已存在于前端:如果表格数据已经以 JSON 或其他形式存在于前端,可以直接利用前端技术将其导出为 Excel、CSV 或其他格式。
- 实时生成/计算:如果导出的表格需要根据用户输入或动态生成,可以使用前端技术基于用户操作实时生成表格,并提供导出功能。
- 快速响应:前端导出表格可以提供更快的响应速度,避免等待服务器端的处理和下载时间。
后端导出的场景:
- 大量数据:如果要导出的表格数据量很大,超过了前端处理能力或网络传输限制,那么在服务器端进行导出会更高效。
- 安全性和数据保护:敏感数据不适合在前端暴露,因此在服务器端进行导出可以更好地控制和保护数据的安全。
- 复杂的业务逻辑:如果导出涉及复杂的业务逻辑、数据处理或数据查询,使用服务器端的计算能力和数据库访问更合适。
- 跨平台支持:如果需要支持多个前端平台(如 Web、移动应用等),将导出功能放在服务器端可以提供一致的导出体验。
三、讲解一下在前端做的导出
xlsx、xlsx-style
如果是只做表格导出:www.npmjs.com/package/xls…
如果导出要包含样式:www.npmjs.com/package/xls…
javascript
package.json
json
"xlsx": "^0.15.5", "xlsx-style": "^0.8.13"
大概效果如下:
感觉前端导出也很容易。
哦哦,那你别高兴太早。
四、需求升级:单元格要居中和加粗。
xlsx
尝试使用xlsx-style设样式。
官方文档:github.com/rockboom/Sh…
文档说给单元格设置s为对象
javascript
发现设置无效。
有人说要改xlsx、xlsx-style源码:
大概的意思是:修改xlsx.extendscript.js、xlsx.full.min.js更改文件变量。
发现仍然无效。
使用binary方式保存
- 首先保存的时候 type要改成 binary方式
- 保存的时候需要使用 xlsx-style模块
javascript
复制代码
var writingOpt = { bookType: 'xlsx', bookSST: true, type: 'binary' // <--- 1.改这里 } /* 2. type:'array'改为'binary' 后因为下面代码会报错, 打不开excel new Blob([wbout], { type: 'application/octet-stream' } 要文本转换成数组缓存后再生成二进制对象 */ // 添加String To ArrayBuffer function s2ab(s) { var buf = new ArrayBuffer(s.length); var view = new Uint8Array(buf); for (var i = 0; i < s.length; i++) { view[i] = s.charCodeAt(i) & 0xFF; } return buf; } let blob = new Blob([s2ab(wbout)], { type: 'application/octet-stream' }) FileSaver.saveAs(blob, exportName)
可以下载了。但依然样式没起作用。
使用 xlsx-style 模块生成文件
首先安装模块
复制代码
npm install xlsx-style
在项目里安装报好多错误直接强制安装,不检查依赖。
复制代码
npm install xlsx-style -force
安装完成后 找不到cptable模块会报错
报错内容如下:
bash
复制代码
./node_modules/xlsx-style/dist/cpexcel.js Module not found: Error: Can't resolve './cptable' in
这个问题在vue.config.js里配置一下就可以解决。
其他框架自己找找方法,反正只要不让他报错能启动就行。
javascript
复制代码
module.exports = { // ...其他配置省略 configureWebpack: { // ...其他配置省略 externals:{ './cptable':'var cptable' }, },
安装完xlsx-style后改代码
javascript
复制代码
import XLSX2 from "xlsx-style"; // 1. 引入模块 // 2. 使用`xlsx-style` 生成。 XLSX.write => XLSX2.write var wbout = XLSX2.write(wb, writingOpt)
仍然无效。
总结xlsx
大概的意思是说:默认不支持改变样式,想要支持改变样式,需要使用它的收费版本。
本着勤俭节约的原则,很多人使用了另一个第三方库:xlsx-style[4] ,但是使用起来极其复杂,还需要改 node_modules 源码,这个库最后更新时间也定格在了 6年前。还有一些其他的第三方样式拓展库,质量参差不齐。
使用成本和后期的维护成本很高,不得不放弃。
ExcelJS
ExcelJS终于可以了
ExcelJS[5] 周下载量 450k,github star 9k,并且拥有中文文档,对国内开发者很友好。虽然文档是以README 的形式,可读性不太好,但重在内容,常用的功能基本都有覆盖。
最近更新时间是6个月内,试用了一下,集成很简单,再加之文档丰富,就选它了。
arduino
复制代码
npm install exceljs npm install file-saver // 下载到本地还需要另一个库:file-saver
基本操作
javascript
复制代码
//导入ExcelJS import ExcelJS from "exceljs"; //下载文件 download_file(buffer, fileName) { console.log("导出"); let fileURL = window.URL.createObjectURL(new Blob([buffer])); let fileLink = document.createElement("a"); fileLink.href = fileURL; fileLink.setAttribute("download", fileName); document.body.appendChild(fileLink); fileLink.click(); }
导出xlsx表格的代码
javascript
复制代码
//下面是导出的函数 async export() { const workbook = new ExcelJS.Workbook(); const worksheet = workbook.addWorksheet("Sheet1"); //这里是数据列表 const data = [ { id: 1, name: "艾伦", age: 20, sex: "男", achievement: 90 }, { id: 2, name: "柏然", age: 25, sex: "男", achievement: 86 }, ]; // 设置列,这里的width就是列宽 worksheet.columns = [ { header: "序号", key: "id", width: 10}, { header: "姓名", key: "name", width: 10 }, ]; // 批量插入数据 data.forEach(item => worksheet.addRow(item)); // 写入文件 const buffer = await workbook.xlsx.writeBuffer(); //下载文件 this.download_file(buffer, "填报汇总.xlsx"); }
设置行高和列宽
列宽上面已经有了,这里说明一下行高怎么设置
worksheet.getRow(2).height = 30;
合并单元格
worksheet.mergeCells("B1:C1");
自定义表格样式
javascript
复制代码
//设置样式表格样式,font里面设置字体大小,颜色(这里是argb要注意),加粗 //alignment 设置单元格的水平和垂直居中 const B1 = worksheet.getCell('B1') B1.font = { size: 20, color:{ argb: 'FF8B008B' }, bold: true } B1.alignment = { horizontal: 'center', vertical: 'middle' }
ExcelJS实战
javascript
复制代码
import ExcelJS from "exceljs"; //下载文件 download_file(buffer, fileName) { console.log("导出"); let fileURL = window.URL.createObjectURL(new Blob([buffer])); let fileLink = document.createElement("a"); fileLink.href = fileURL; fileLink.setAttribute("download", fileName); document.body.appendChild(fileLink); fileLink.click(); }, async exportClick() { const loading = this.$loading({ lock: true, text: "数据导出中,请耐心等待!", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", }); this.tableData = [ { a: 1, b:2 } ] const enterpriseVisitsColumns = [ { prop: "a", label: "银行", }, { prop: "b", label: "企业数", } ] // 表格数据:this.tableData if (!(this.tableData && this.tableData.length)) { this.$message.info("暂无数据"); loading.close(); return; } let tableName = this.tableName; // 表格名 const workbook = new ExcelJS.Workbook(); const worksheet = workbook.addWorksheet(tableName); const props = enterpriseVisitsColumns(); //这里是数据列表 const data = this.tableData; // 设置列,这里的width就是列宽 let arr = []; props.forEach((p) => { arr.push({ header: p.label, key: p.prop, width: 25, }); }); worksheet.columns = arr; // 插入一行到指定位置,现在我往表格最前面加一行,值为表名 const rowIndex = 1; // 要插入的行位置 const newRow = worksheet.insertRow(rowIndex); // 设置新行的单元格值 newRow.getCell(1).value = tableName; // 值为表名 // 批量插入数据,上面插一条,这里就是从第二行开始加 data.forEach((item) => worksheet.addRow(item)); //设置样式表格样式,font里面设置字体大小,颜色(这里是argb要注意),加粗 //alignment 设置单元格的水平和垂直居中 // const B1 = worksheet.getCell("B1"); // B1.font = { size: 20, color: { argb: "FF8B008B" }, bold: true }; // B1.alignment = { horizontal: "center", vertical: "middle" }; // 合并单元格,就是把A1开始到J1的单元格合并 worksheet.mergeCells("A1:J1"); // 批量设置所有表格数据的样式 worksheet.eachRow((row, rowNumber) => { let size = rowNumber == 1 ? 16 : rowNumber == 2 ? 12 : ""; //设置表头样式 row.eachCell((cell) => { cell.font = { size, // color:{ argb: 'FF8B008B' }, bold: true, }; cell.alignment = { horizontal: "center", vertical: "middle" }; }); //设置所有行高 row.height = 30; }); // 写入文件 const buffer = await workbook.xlsx.writeBuffer(); //下载文件 this.download_file(buffer, tableName + ".xlsx"); loading.close(); },
后记
导出功能并不是说都是前端或者后端实现,要具体情况,具体分析,我相信哪方都可以做,但谁适合做,这个才是我们需要去思考的。
就如同我们项目中,该例子后面也是前端实现的,大数据分页当然还是得后端同学来实现较好。
如果有其他更好的方法也欢迎评论区见,这里提供的只是诸多方法之一。
最后,祝君能拿下满意的offer。
我是Dignity_呱,来交个朋友呀,有朋自远方来,不亦乐乎呀!深夜末班车
👍 如果对您有帮助,您的点赞是我前进的润滑剂。