导出 CSV
JavaScript 导出 CSV 比较简单,只需要根据 CSV 的格式进行逗号分隔和换行,最后将文本保存成 CSV 文件即可。
需要注意的是,Excel 默认使用 ANSI 编码来打开 CSV 文件,如果 CSV 文件使用 UTF-8 编码,使用 Excel 打开就会乱码,解决方法是使用 Excel 中 数据 - 导入自文件 向导来导入 UTF-8 编码的 CSV 文件,这样就可以正常显示 Unicode 字符了。但这相比直接打开麻烦了很多,另一个办法是将 CSV 文件保存成 UTF-8 BOM 编码,这样 Excel 就会以 UTF-8 编码读取文件,不会出现乱码了。
BOM (Byte Order Mark) 是一段特定的十六进制序列,在 UTF-16 和 UTF-32 中用来标识该字节流的字节序,是高位在前还是低位在前,在 UTF-8 中 BOM 一般用来标识文件采用 UTF-8 编码,但并不是必要的。
UTF-8 的 BOM 的十六进制表示为 EF BB BF ,也可以用一个 Unicode 字符表示: U+FEFF 。所以我们只要在 CSV 文件开头加入 BOM 就可以将文件保存为 UTF-8 BOM 格式。可以直接在 Buffer 中先加入 \xEF\xBB\xBF 作为开头 ,也可以在字符串开头加入 \uFEFF 这个 Unicode 字符。  
function writeCSV() {
    let csvContent = '\uFEFF';
    csvContent = csvContent.concat("布洛妮娅萌");
    fs.writeFileSync('konata.csv', csvContent);
}
导出 xls 和 xlsx
导出 xls 和 xlsx,肯定要使用库了,目前比较好用的是 SheetJS,它分为专业版和社区版,专业版主要增加了样式设置、插入图片图表的功能,据说价格很贵,一般情况下社区版足够使用了。社区版也叫做 js-xlsx,支持数据读取、解析处理、数据导出,同时支持浏览器端和 Node 端。
SheetJS 常用 API
workbook 和 worksheet
首先要明确的两个概念是 workbook 和 worksheet , 这两个对象分别对应了整个文件(工作簿)和一个文件中的一个表格(工作表)。
workbook
workbook 有两种方式来获取:
- 读取已有的文件,返回一个 
workbook对象。 - 通过 
XLSX.utils.book_new ()创建空白的workbook对象。 
常用到的 workbook 的属性有两个:
Sheets:文件中的表格列表。SheetNames:文件中的表格名列表,表现为数组。需要获取
workbook中的某个表格时,可以这样获取:
let sheetname = workbook.SheetNames[0];
let worksheet = workbook.Sheets[sheetname];
worksheet
worksheet 对象代表文件中的一个表格,可以通过下标的形式访问表格中任意一小格的值:
worksheet['A1']
其返回值为一个对象,一般具有两个属性:
v:当前小格的值。t:当前小格值的类型。
读取文件
SheetJS 通过两种方式读取文件内容:
XLSX.read (data, read_options):读取data并解析。XLSX.readFile (filename, read_options):读取filename文件并解析。有关
read_options的内容详见 read_options。
写入数据
SheetJS 通过三种方法写入数据,这两种方法均会对数字、字符串、 null 和 undefined 、日期等类型进行自动解析:
XLSX.writeFile (workbook, filename [, write_options]):按照workbook对象生成文件。若在浏览器端,会自动下载该文件。在 Node 端,会自动生成该文件并保存到本地XLSX.writeFileAsync (filename, workbook, o, cb): 按照workbook对象生成文件。当o执行完毕后,调用cb回调函数。有关
read_options的内容详见 read_options。
// XLSX.writeFile
// 第一个参数为一个 workbook 对象,第二个参数为所要生成文件的文件名
XLSX.writeFile(workbook, 'out.xlsb');
// XLSX.write
// 第一个参数为一个 workbook 对象,第二个参数是对生成文件格式的一些设置
// 在本例中,生成格式为 xlsx 的文件,编码为 base64
XLSX.write(workbook, {
    bookType: 'xlsx',
    bookSST: false,
    type: 'base64'
})
// 由于其不生成文件,而只是返回组成文件的数据,所以很适合一些需要通过异步请求来修改服务器上的文件等场景
转换数据格式
SheetJS 支持读取文件并把数据导出成任意格式,可通过以下几个 API 完成,传入的参数均是一个 worksheet 对象
XLSX.utils.sheet_to_csv (worksheet):将表格数据转化为 CSV 格式。XLSX.utils.sheet_to_txt (worksheet):将表格数据转化为生成由 UTF-16 编码的 txt 格式。XLSX.utils.sheet_to_html (worksheet):将表格转化为 html 文件。XLSX.utils.sheet_to_json (worksheet): 将表格数据转化为 json 格式。
表格操作
XLSX.utils.aoa_to_sheet (Array [][]):将二维数组转化为worksheet对象。XLSX.utils.json_to_sheet (Object):将 js 对象转化为worksheet对象。XLSX.utils.table_to_sheet (HTML):将 DOM 节点转化为worksheet对象(一般为table元素、tr元素和th元素)。XLSX.utils.sheet_add_aoa (worksheet, Array [][]):将二维数组中的数据添加到已有的worksheet中。XLSX.utils.sheet_add_json (worksheet, Object):将 js 对象中的数据添加到已有的worksheet中。XLSX.utils.book_append_sheet (workbook, worksheet, sheetname):将worksheet对象添加到workbook中,并命名为sheetname。
将网页中的 table 元素转换成 xlsx 并下载
引入 SheetJS:
<script lang="javascript" src="dist/xlsx.full.min.js"></script>
JavaScript:
function exportExcel() {
    const sheet = XLSX.utils.table_to_sheet(document.getElementsByTagName("table")[0]); // 将网页中第一个 table 元素转换为工作表对象
    let filename = document.getElementById("table-title").innerText; // 得到文件名
    openDownloadDialog(sheet2blob(sheet), filename + '.xlsx'); // 文件下载
}
// 将一个 sheet 转成最终的 excel 文件的 blob 对象,然后利用 URL.createObjectURL 下载
function sheet2blob(sheet, sheetName) {
    let workbook = {
        SheetNames: [sheetName],
        Sheets: {}
    };
    workbook.Sheets[sheetName] = sheet;
    // 生成 excel 的配置项
    const wopts = {
        bookType: 'xlsx', // 要生成的文件类型
        bookSST: false, // 是否生成 Shared String Table,官方解释是,如果开启生成速度会下降,但在低版本 IOS 设备上有更好的兼容性
        type: 'binary'
    };
    let wbout = XLSX.write(workbook, wopts);
    let blob = new Blob([s2ab(wbout)], {
        type: "application/octet-stream"
    });
    // 字符串转 ArrayBuffer
    function s2ab(s) {
        let buf = new ArrayBuffer(s.length);
        let view = new Uint8Array(buf);
        for (let i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
        return buf;
    }
    return blob;
}
/**
 * 通用的打开下载对话框方法,没有测试过具体兼容性
 * @param url 下载地址,也可以是一个 blob 对象,必选
 * @param saveName 保存文件名,可选
 */
function openDownloadDialog(url, saveName) {
    if (typeof url == 'object' && url instanceof Blob)
        url = URL.createObjectURL(url); // 创建 blob 地址
    const aLink = document.createElement('a');
    aLink.href = url;
    aLink.download = saveName || ''; // HTML5 新增的属性,指定保存文件名,可以不要后缀,注意,file:///模式下不会生效
    let 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);
}
HTML:
<button class="button is-link margin-btn" onclick="exportExcel()">
    导出 Excel
</button>