在作以前一个项目的时候,须要有生成excel表格的功能,在网上查询一番后,发现不少人都推荐Sheetjs的xlsx.js这款工具,本身使用了一下,感受也不错,上手简单,功能结构清晰。所以,在这里就我本身使用到的功能部分和使用时候出现问题简单总结一下。前端
过去的时候,excel表格的生成一般是由后端完成的,主要缘由是以前我的PC的性能较低,完成数据量过大的excel表格生成工做比较困难,耗时太长,会形成长时间浏览器的卡顿,对用户体验形成不好的影响。
而如今,我的PC的性能已经有很大的提高,数据量较大的excel表格生成不会对用户体验形成较大的影响(多少仍是会有影响的,可是已经没有那么夸张了,并且生成大数据量excel的功能通常是在网站的后台,用户每每可以接受必定时间的等待)。让用户PC机去处理excel的生成,可以解放服务器的很大压力,因此如今不少网站选择把生成excel的工做交付给前端去完成。git
纯js便可读取/生成excel,功能强大,支持多种格式,兼容性高。
xlsx.js有core和full两个版本,使用xlsx.core.min.js版本基本上就能知足大部分需求,我在项目中选择了core的版本。
其余详细介绍能够去看官方github:https://github.com/SheetJS/sheetjs。github
引入js文件后,会在window对象上挂载一个全局对象XLSX,全部的操做函数都经过这个全局对象调用。json
workbook对象是描述一个excel文件的基本对象。
workbook对象中,主要包含了如下内容:后端
* SheetNames数组:excel文件包含的全部sheets名称列表 * Sheets数组:excel文件包含的全部sheets的内容(worksheet对象列表)
worksheet对象则包含了一个sheet表格的详细内容。
对象中包含的内容包括:数组
* !ref:单元格范围 * !rows:表格行属性 * !cols:单元格属性 * !merges:单元格合并 * A1/B1/C1...:对应excel中的每个具体的单元格
想要生成一个excel文件,须要首先构建出一个workbook对象,而后再对这个对象进行其余的操做。
workbook对象与worksheet对象看起来比较复杂,不少人看到后会比较焦虑:这些难道要我本身去组织吗?其实,其中包含的不少项都是能够经过内置方法自动生成的,因此不要太担忧。浏览器
在个人项目中,需求是生成一个带有格式要求的excel表格,这里我们先来讲说表格内容部分的生成。
xlsx.js提供了多种函数,将不一样结构的数据转换成为worksheet对象,包括:缓存
* XLSX.utils.aoa_to_sheet:数组转换为worksheet * XLSX.utils.json_to_sheet:json对象转换为worksheet * XLSX.utils.table_to_sheet:表格转换为worksheet
个人项目中,根据项目状况选择了由数组转换为worksheet的方式,这里我就详细说一下如何构建符合要求的数据数组并将其转换为worksheet对象,其余的方式你们能够移步官方github去查看。服务器
基本的数组结构为:app
var arr = [ [单元格A1内容,单元格A2内容,单元格A3内容,...], [单元格B1内容,单元格B2内容,单元格B3内容,...], [单元格B1内容,单元格B2内容,单元格B3内容,...], ... ]
因此,理论上,咱们只要不断的向数组里push内容就能够了,数组内容填充完成以后,直接调用var ws = aoa_to_sheet(arr)
,就可以输出一个worksheet对象到变量ws
中了。ws
对象中的!ref
属性是自动生成的。
咱们要输出的表格,并不都是单一罗列式的,每每会有复杂的格式,这就须要咱们经过占位与合并单元格来控制表格的格式。
好比,咱们但愿输出的表格是这个样子的:
表格中的表头,存在二级分类,好比GPS位置下还包含了纬度、经度两个字段。
这个时候,构建数组表头部分时,咱们须要在一些地方,好比表头第二行的开头,使用null
来占位,保证表头上下两行列的一一对应。
var arr = [ ['户主姓名', '性别', '文化程度', '手机号码', 'GPS位置', 'null', '户籍所在地', 'null', 'null', 'null'], [null, null, null, null, '纬度', '经度', '盟', '旗', '苏木', '嘎查'] ];
这个时候,咱们获得的结果是这个样子的:
接下来,咱们要作的就是合并单元格,好比A1和B一、A2和B二、A5和A6等。
咱们须要向生成的ws对象中手动的加入属性!merges
及其对应的数组。
ws['!merges'] = [ {s: {r: 0, c: 0}, e: {r: 1, c: 0}}, {s: {r: 0, c: 1}, e: {r: 1, c: 1}}, {s: {r: 0, c: 2}, e: {r: 1, c: 2}}, {s: {r: 0, c: 3}, e: {r: 1, c: 3}}, {s: {r: 0, c: 4}, e: {r: 0, c: 5}}, {s: {r: 0, c: 6}, e: {r: 0, c: 9}} ]
数组的每一项,表明其中一个单元格的合并要求。每个对象中:s表明合并的起始位置,e表明合并的结束位置。
在s中,r表明行数,c表明列数。
好比数组的第一个对象,表达的含义为:以0行0列(对应单元格A1)做为起始,以1行0列(对应单元格A2)做为结束,合并这些单元格。
当某一个单元格字符数过长的时候,显示的内容会超过单元格边界,很是影响用户的阅读,这个时候,须要经过调整行高或者列宽来保证表格便于阅读。
咱们须要向生成的ws对象中手动加入属性!cols
来控制列宽,!rows
来控制行高。
例如:
ws['!cols'] = [ {wpx: 90}, {wpx: 50}, {wpx: 60}, {wpx: 100}, {wpx: 100}, {wpx: 100}, {wpx: 70}, {wpx: 100}, {wpx: 100}, {wpx: 100} ];
数组的每一项对应须要控制的列,好比下标0的项表明第一列(A列)的宽度。
wpx表示屏幕像素值,wch表明英文字符数,还有一个width可选,文档描述为 width in Excel's "Max Digit Width",具体含义还没弄明白,求大佬来解释。
行高的控制相似,具体内容请本身查阅文档。
对于workbook对象,咱们只须要填充SheetNames属性和Sheets属性便可。
var workbook = { SheetNames: ['本地户主信息'], Sheets: { '本地户主信息': ws } };
而后,经过XLSX.write
函数生成excel文件的源码(具体应该叫什么我也不清楚,求大佬告知),把文件源码字符串转成arrayBuffer,再经过new Blob
方法转换为二进制,最后使用URL.createObjectURL
函数将blob对象建立为blob地址,赋给a标签的href属性,就可以进行下载了。
这个部分的具体代码,是我从网上直接找的(对于arrayBuffer和blob这块比较生疏),这里贴出来,感谢最初编写这段代码的大佬。
//导出excel相关函数 sheet2blob: function(sheet, sheetName) { sheetName = sheetName || 'sheet1'; var workbook = { SheetNames: [sheetName], Sheets: {} }; workbook.Sheets[sheetName] = sheet; // 生成excel的配置项 var wopts = { bookType: 'xlsx', // 要生成的文件类型 bookSST: false, // 是否生成Shared String Table,官方解释是,若是开启生成速度会降低,但在低版本IOS设备上有更好的兼容性 type: 'binary' }; var wbout = XLSX.write(workbook, wopts); var blob = new Blob([s2ab(wbout)], {type:"application/octet-stream"}); // 字符串转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; } return blob; }, openDownloadXLSXDialog: function(url, saveName){ if(typeof url == 'object' && url instanceof Blob){ url = URL.createObjectURL(url); // 建立blob地址 } var aLink = document.createElement('a'); aLink.href = url; aLink.download = saveName || ''; // HTML5新增的属性,指定保存文件名,能够不要后缀,注意,file:///模式下不会生效 var event; if(window.MouseEvent) event = new MouseEvent('click'); else{ event = document.createEvent('MouseEvents'); event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); } aLink.dispatchEvent(event); } var blob = sheet2blob(ws, '户主信息'); openDownloadXLSXDialog(blob, '123.xlsx');
一个很蛋疼的需求,须要我在一个sheet中重复输出带复杂表头的表格。
在上边构建一个worksheet对象中描述的表格,我只须要输出一个表头便可,我可以提早知道合并哪几个单元格,所以我先将数据部分直接循环插入数组就行,在数组内容分填充完毕以后,生成ws对象,再给ws对象增长的!merges
属性。
而新需求没法这么作了,由于除了向数组中循环输出数据以外,我还须要循环输出表头到数组中。没有办法,这个时候只能先建立!merges
属性的数组,在表格内容数组填充的循环体中,自行计算输出到的行数,缓存在变量里,而后在这个循环体中使用行数变量动态填充!merges
数组,保证单元格合并的正确。