顾名思义,它是用来处理电子表格数据的,我们可以使用
xlsx读取,操作表格数据,并且根据数据生成电子表格文件;对于前端还可以实现dom(table元素)与电子表格数据之间的相互转化
注意:在下面我们所说的dom都指的是table表格元素
在使用之前我们需要了解两个概念:
workbook:我们将它看做一个存放日记的盒子。worksheet:盒子里面存放着我们的日记,而日记内容肯定是写在一张纸上的,每张纸就是一个worksheet,纸上的日记内容就可以理解为表格中存在的数据,盒子中当然可以存放很多日记(暗含我们可以向workbook中添加多个worksheet),
有了上面两个概念的理解,我们所需要做的无非就是写一篇日记然后将日记存放在盒子中,当然我们正在写或者已经存在的日记难免出错,我们还可以进行修改,删除等操作,那么接下来我们就来介绍一下xlsx提供的api是如何帮助我们进行对日记操作使用的吧!(不介绍安装)
我们想要存放日记,首先要有一个盒子(workbook)这个盒子是可以是新的,也可以是已经存在日记的
XlSX.utils.book_new:可以帮助我们创建一个新的wookbook,也就是说这是一个新的盒子。XlSX.utils.table_to_book:这个api可以帮助我们根据dom生成一个workbook,意思就是我们有了一个盒子,并且这个盒子中存在一个日记 (无论这个日记内容是否为空)
// 一、创建一个新的盒子
const workbook = XLSX.utils.book_new();
// 二、已经存在日记内容的盒子
const workbook = XLSX.utils.table_to_book(tableElement);
// 举例我们的dom结构如下
// 步骤二将为我们创建一个盒子,并且里面存在一张名字叫`Sheet1`(后面介绍)纸,
// 这个纸上可以有内容(`table`存在子元素)也可以没有内容(`table`不存在子元素)
<table id="table">
<thead>
<tr>
<th>姓名</th>
<th>年龄</th>
</tr>
</thead>
<tbody>
<tr>
<td>阿坤</td>
<td>18</td>
</tr>
</tbody>
</table>
有了盒子,接下来我们当然要开始写日记了,不过在写日记之前我们肯定要有一张纸(worksheet),我们可以在纸上添加我们的内容。xlsx为我们提供了三种创建worksheet的方法
在我们进行
api创建worksheet之前,我们先来了解一下表格结构如图所示,每一个单元格对应一个坐标(左上角的第一个单元格我们可以用
A1来表示它,其它的单元格以此类推),表示方式=横坐标的大写英文字母+垂直坐标上的序号
XlSX.utils.aoa_to_sheet(data(any[])[],options):根据表格结构生成一个日记,data是一个数组,表示的是一行的数据,数组中的每一个元素对应表格结构中一个单元格的内容,data[]表示表格的多行数据,举例如下
const worksheet = XLSX.utils.aoa_to_sheet([
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
]);
生成的表格如下:
options配置项主要是对单元格的一些设置,这里不详细介绍
XlSX.utils.json_to_sheet(any[],options):根据json结构生成一个日记。
第一个参数数组中的每一项应该是一个对象,该数组中所有对象的不重复key值组成了电子表格的第一行数据,每个对象的所有value组成电子表格的一行数据,并且从电子表格的第二行开始垂直排列,垂直排列的顺序为数组中对象的排列顺序,示例如下
const worksheet = XLSX.utils.json_to_sheet([
{
姓名: '张三',
年龄: 1,
},
{
姓名: '李四',
年龄: 12,
性别: '男',
},
]);
生成的表格
上图生成的电子表格可以看出,数组中所有对象的不重复键名
["姓名","年龄","性别"]组成了表格的第一行,而对象的所有value组成了电子表格的其他行,并且排列顺序从电子表格的第二行开始,["张三",1]构成了第二行,["李四",12,"男"]组成了电子表格的第三行,如果有更多的数据将以此类推...
options对象为配置对象,我们在这里只介绍两个属性skipHeader和header:
skipHeader:可以理解为是否不展示第一行,当设置为true的时候,将不展示第一行,,继续以上述代码为例
const sheet = XLSX.utils.json_to_sheet(
[
{
姓名: '张三',
年龄: 1,
},
{
姓名: '李四',
年龄: 12,
性别: '男',
},
],
{
skipHeader: true,
},
);
生成的表格如下:
可以看到第一行消失了。
在介绍配置属性
header之前,我们先介绍一下电子表格中第一行的排列顺序与json对象的关系:默认第一行的排列顺序为Object.keys(Object.asign(...sheetJson)),意思就是说将要转成sheet的json数组中的所有对象使用Object.assign进行合并,然后将合并后的对象通过Object.keys后获取键名的数组,这个数组的排列就是第一行的排列顺序,单元格中的内容对应生成数组中的元素
header:当我们获取到sheetJson默认生成的key数组排列顺序不是我们想要的时候,我们可以通过header属性进行自定义,该属性值的类型为string[]。
我们还以上述为例,已知要转化为sheet的json数组对象,我们根据默认行为得知生成的第一行数据为["姓名","年龄","性别"],现在我们想要调整姓名和年龄的位置,我们可以通过header属性实现这一功能,如下:
const sheet = XLSX.utils.json_to_sheet(
[
{
姓名: '张三',
年龄: 1,
},
{
姓名: '李四',
年龄: 12,
性别: '男',
},
],
{
header: ['年龄', '姓名', '性别'],
},
);
生成的电子表格:
注意:
header属性有个特点,当header数组中的的元素在默认key数组中都存在的时候,header数组的作用仅仅是调整默认key数组中元素的位置,若header数组中存在默认key数组没有的元素的时候,执行的操作是将没有的元素添加到第一行中默认的插入位置是在unshift到默认key数组中,以下列举几种情况方便理解(以上述代码为例)
["年龄","姓名","性别"]:这种的仅仅是调整位置,调整了姓名和年龄的位置["手机号","姓名","年龄","性别","住址"]:这种的是在默认key数组的前后各添加一个不存在的属性,效果如下["手机号","姓名","住址","年龄"]:这种的是在默认key数组的前面以及姓名和年龄个添加一个不存在的属性,效果如下(可以看出后续内容不需要改变的话可以省略)
注意:
reader数组中的元素将会从默认key数组的第一个元素开始修改,我们可以省略后续不改变的元素,但排在前面的元素不能省略,假设我们的我们的默认元素是["手机号","姓名","住址","年龄"],如果我们想要在姓名和地址之间插入一个身高我们就必须把前面的手机号排在前面["手机号","姓名","身高","住址"],后面的年龄可以省略。
XlSX.utils.table_to_sheet(dom,options):根据dom生成一个日记(worksheet)
举例我们的dom结构如下:
<table id="table">
<thead>
<tr>
<th>姓名</th>
<th>年龄</th>
</tr>
</thead>
<tbody>
<tr>
<td>张三</td>
<td>18</td>
</tr>
</tbody>
</table>
// 根据dom获取worksheet
const worksheet = XLSX.utils.table_to_sheet(dom);
生成的表格如下:
当我们写完日记的时候肯定要将日记放在盒子中保存起来,xlsx为我们提供了将worksheet放到workbook中的方法
XLSX.utils.book_append_sheet(workbook, worksheet, sheetname, roll)workbook:可以理解为要放日记的盒子。worksheet:放入盒子中的日记。sheetname:日记的名称(方便在盒子中找到这篇日记),如果为undefind,默认生成Sheet加数字的格式名称。roll:是一个boolean值,当设置为true时候,函数将以指定的工作表名称开头。如果工作簿中存在工作表名称,将通过查找名称词干并递增计数器来选择新的工作表名称,下面的例子可以帮助我们理解这句话:
- 假设我们有一个空的盒子(
newWorkbook),然后我们写好了一篇日记(sheet),现在我们要将日记放进盒子中,但是呢由于粗心大意,我们忘记给日记起名字了,这个时候我们的盒子有一个神奇的魔法,它可以帮助我们默认给日记起一个叫Sheet1的名字,如果我们隔三差五写的日记都忘记了起名字,那么盒子将按照自己的默认行为,递增给日记起名字,例如Sheet2、Sheet3...。- 假设我们写了一篇日记,并且赋予了它一个名字,但是我们忘记我们的盒子中是否存在过名称一样的日记,为了保证我们在盒子中查找日记的唯一性,盒子默认具有排除重名日记的功能,当我们使用
book_append_sheet添加重复名称sheet的时候,将会抛出错误Uncaught Error: Worksheet with name |重复sheet的名称| already exists!- 假如我们给新写的日记命名了一个重复的名字,但是我们也想要添加到盒子中,我们可以将
roll设置为true,盒子中的日记名称是唯一的,不能有重复,盒子为了保证日记名称的唯一性,同时也满足我们的要求,盒子会将重复添加进来的日记名称进行递增处理,例如开心这个名称重复了,盒子会将它递增处理为开心1,以此类推开心2、开心3...
到目前为止,我们掌握了如何获取一个盒子(workbook)和日记(worksheet),以及如何将日记(worksheet)添加到盒子(workbook)中,那么接下来我们开始介绍如何从盒子(workbook)中获取以及编辑日记(worksheet)
有一天我们突然想要回味一下一篇日记的内容,那么首先我们需要知道我们的日记放在了哪个盒子中,以及日记的名称,这样才能定位找到这篇日记
workbook.Sheets[sheetName]:可以帮助我们通过日记名称获取日记。
假如我们日记太多忘记了日记的名称,我们只有看到日记名称的时候才能知道我们想看的究竟是哪一篇,盒子当然也很聪明,它将所有的日记名称整理了起来并放在了一起
workbook.SheetNames:获取所有盒子中日记的名称,我们可以通过这些名称再通过上面的对象获取对应的日记
在写日记的时候难免出现遗漏或错误,我们可以拿回来对内容进行编辑
XLSX.utils.sheet_add_aoa(worksheet, aoa, opts):可以帮助我们向worsheet中通过电子表格结构(上面有介绍)的形式添加内容worksheet:要编辑的日记。aoa:插入的数据opts:配置项。
假如我们有一个表格如下:
现在我们想要插入一个年龄20岁,名字叫李四的人,我们可以这样
XLSX.utils.sheet_add_aoa(sheet, [['李四', 20]]);
生成图片如下:
结果我们发现数据插入在了A1和B1,并且将原本在电子表格中的数据替换掉了(这符合XLSX.utils.aoa_to_sheet第一个参数的插入行为),但这种结果并不是我们想要的,我们期望的是数据插入在A3和B3,这时候我们需要用到opts配置项中的origin属性,他有4种赋值方式(XLSX.utils.sheet_add_aoa默认采用的是第二种方式,origin:'A1'),具体如下
cellAddress:开始插入位置的单元格行列信息,有两个参数r(row)和c(column),分别表示插入的行和列,他们的值都是从0开始的,0表示电子表格中的第一行(列),在本例中我们可以使用origin:{c:0,r:2}实现目的A1-style 字符串:表示开始插入单元格的位置信息,比如A1,在本例中,可以用origin:'A3'实现目的number>=0:表示从number行的第一列开始插入,在本例中我们可以使用origin:2实现目的-1:也是数字类型,表示的是从电子表格的最后一行的下一行的第一列开始插入,也就是在电子表格的最后插入一行,并且插入位置为这一行的第一列开始的,在本例中我们也可以用这个origin:-1实现目的
此方法还可以修改我们单元格的值,比如我们想要修改张三的年龄为25岁,我们可以:
XLSX.utils.sheet_add_aoa(sheet, [[25]], { origin: 'B2' });
效果如下:
-
XLSX.utils.sheet_add_json(worksheet, json, opts):可以帮助我们向worsheet中通过json结构(上面有介绍)的形式添加内容。opts部分参数解释:header:同XLSX.utils.json_to_sheet配置项中的header。origin:同XLSX.utils.aoa_to_sheet配置项中的origin。
-
XLSX.utils.sheet_add_dom(worksheet, dom, opts):可以帮助我们向worsheet中通过dom结构(上面有介绍)的形式添加内容,origin配置项同上描述opts部分参数解释:origin:同XLSX.utils.aoa_to_sheet配置项中的origin。display:boolean值,意思是是否分析dom中隐藏的行(tr标签设置hidden属性)或者列(td标签设置hidden属性)中的数据(实例请看下方),默认为false,即分析隐藏的单元格或行。raw:boolean值,当设置为true的时候dom中的数据将保持为字符串,不会进行类型转化,举例如下:
假如我们的dom结构如下:
<table id="table">
<thead>
<tr>
<th>姓名</th>
<th>年龄</th>
</tr>
</thead>
<tbody>
<tr>
<td>王五</td>
<td>22</td>
</tr>
</tbody>
</table>
---------------------------演示对于配置属性raw属性的理解----------------------⬇️
将数据插入到表格中(默认行为)
XLSX.utils.sheet_add_dom(worksheet, dom);
控制台输入(或终端打印)如下:
我们可以看到22的类型是number类型(默认进行了转化),这里的A1、A2...的值是一个单元格对象,这个对象里面可以包含的属性如下
type ExcelDataType = 'b' | 'n' | 'e' | 's' | 'd' | 'z';
export interface CellObject {
// 单元格值
v?: string | number | boolean | Date;
/** Formatted text (if applicable) */
w?: string;
/**
* 单元格数据类型.
* b Boolean, n Number, e Error, s String, d Date, z Empty
*/
t: ExcelDataType;
/** Cell formula (if applicable) */
f?: string;
/** Range of enclosing array if formula is array formula (if applicable) */
F?: string;
/** 富文本 (if applicable) */
r?: any;
/** HTML rendering of the rich text (if applicable) */
h?: string;
/** Comments associated with the cell */
c?: Comments;
/** Number format string associated with the cell (if requested) */
z?: NumberFormat;
/** Cell hyperlink object (.Target holds link, .tooltip is tooltip) */
l?: Hyperlink;
/** The style/theme of the cell (if applicable) */
s?: any;
}
当我们将配置项row设置为true再次打印:
我们可以看到B2的类型变为了字符串,不再是数字类型
---------------------------演示对于配置属性raw属性的理解----------------------⬆️
---------------------------演示对于配置属性display属性的理解----------------------⬇️
现在我们隐藏王五这个单元格,代码如下:
<table id="table" style={{ border: '1px solid #ccc' }}>
<thead>
<tr>
<th>姓名</th>
<th>年龄</th>
</tr>
</thead>
<tbody>
<tr>
<td hidden>王五</td>
<td>男</td>
</tr>
</tbody>
</table>
在使用display属性的默认值(false)即分析隐藏的单元格属性,打印如下
我们可以看到dom中隐藏了王五,但是打印表格数据的时候仍然分析出了隐藏的数据
将display属性的值设置为true即不分析隐藏的单元格属性,打印如下
我们可以看到王五单元格对象不存在了,这就就是display属性的作用,隐藏行与隐藏单元格的行为一致,这里不再进行演示,有兴趣可以自己验证
---------------------------演示对于配置属性display属性的理解----------------------⬆️
好了,目前我们对于日记的操作已经完成了,接下来该处理盒子了,我们的盒子肯定是需要购买带回来的,否则怎么属于我们的呢,这就好比,我们通过代码将数据处理完成了,那么我们只有将它下载保存下来才真正的属于我们
-
XLSX.writeFile(workbook,filename,opts):打包处理workbook数据然后下载保存文件,文件的格式为filename的后缀名(例如:test.xlsx打包后文件的格式为xlsx),对于opts这里值介绍compression和bookTypebookType:可供支持下载保存的文件格式如下图。compression:是否压缩,压缩后保存下载下来的文件大小将改变,该属性生效的前提是bookType的container=ZIP
| bookTyp | file ext | container | sheets | Description |
|---|---|---|---|---|
xlsx | .xlsx | ZIP | multi | Excel 2007+ XML Format |
xlsm | .xlsm | ZIP | multi | Excel 2007+ Macro XML Format |
xlsb | .xlsb | ZIP | multi | Excel 2007+ Binary Format |
biff8 | .xls | CFB | multi | Excel 97-2004 Workbook Format |
biff5 | .xls | CFB | multi | Excel 5.0/95 Workbook Format |
biff4 | .xls | none | single | Excel 4.0 Worksheet Format |
biff3 | .xls | none | single | Excel 3.0 Worksheet Format |
biff2 | .xls | none | single | Excel 2.0 Worksheet Format |
xlml | .xls | none | multi | Excel 2003-2004 (SpreadsheetML) |
numbers | .numbers | ZIP | single | Numbers 3.0+ Spreadsheet |
ods | .ods | ZIP | multi | OpenDocument Spreadsheet |
fods | .fods | none | multi | Flat OpenDocument Spreadsheet |
wk3 | .wk3 | none | multi | Lotus Workbook (WK3) |
csv | .csv | none | single | Comma Separated Values |
txt | .txt | none | single | UTF-16 Unicode Text (TXT) |
sylk | .sylk | none | single | Symbolic Link (SYLK) |
html | .html | none | single | HTML Document |
dif | .dif | none | single | Data Interchange Format (DIF) |
dbf | .dbf | none | single | dBASE II + VFP Extensions (DBF) |
wk1 | .wk1 | none | single | Lotus Worksheet (WK1) |
rtf | .rtf | none | single | Rich Text Format (RTF) |
prn | .prn | none | single | Lotus Formatted Text |
eth | .eth | none | single | Ethercalc Record Format (ETH) |
其他api的介绍
XLSX.writeFileXLSX:实现和XLSX.writeFile一样的功能XLSX.readFile(path,data):读取路径上的文件内容,只用于node环境,实例如下XLSX.read(data,opts):用于读取数据得到workbook,前端和node中通用,data的格式是js字符串、"二进制字符串"、NodeJS缓冲区或类型化数组(Uint8Array或ArrayBuffer)中的电子表格字节中提取数据,对于node环境中,在前端上传formData数据后可直接读取存储在其中的数据,对于前端,通过文件上传获取得到File对象,而File对象继承Blob对象,Blob对象可以将数据转化为ArrayBuffer,因此前端获取File对象后可以通过File.arrayBuffer()转化后读取,切忌,这个方法会返回一个Promise。XLSX.utils.sheet_to_html(worksheet):将表转化为domXLSX.utils.encode_cell(cell_address):将单元格坐标转化为A1-style的坐标,举例:{r:1,c1}=>B2XLSX.utils.sheet_to_json(worksheet,opts):将表转化为json对象,opts部分配置header:设置生成对象的键名,如果不设置,默认将电子表格的第一行作为键名。"A"值表示的是将A1-style坐标的横坐标作为对应单元格数据的键,举例:B1单元格的数据在生成json数据的时候,他的键是B,C2单元格数据的键是Crange:指定从表格的第几行开始解析默认从第一行开始解析,可以设置两种类型:-
number:0表示第一行,1表示第二行,以此类推.... -
A1-style字符串:上一条有介绍,设置后即将某个单元格数据转化为json
-