前端将html导出为word文档(插件篇)

5,010 阅读1分钟

🔔 支持功能

  1. dom节点导出为图片(包含图片、canvas 图表)
  2. 可以控制导出图片的一些属性,包括背景颜色、大小、画质等
  3. 包含章节导航
  4. 文档的换行

🔨 使用的插件

dom-to-image :实现dom 转 图片

htmlDocx:导出word文档

💡 过程

总思路可以分为3步

步骤1:把一些展示图表的dom节点变成图片

步骤2:把图片插入到dom节点前,然后删除dom节点,得到最终的dom

步骤3:利用htmlDocx导出为word文档

网上搜索把dom节点变成图片的实现方案,大多都是用 html2canvas 插件

然后就封装了以下方法,详细说明请看注释:

📄 导出word第一代代码(html2canvas + htmlDocx )

import htmlDocx from 'html-docx-js/dist/html-docx';
import html2canvas from 'html2canvas';

export function wordExport(id, fileName) {
  let contentNode = document.getElementById(id);
  
  // dom转成canvas,再转成图片
  let cloneContentNode = contentNode.cloneNode(true);//克隆dom节点
  const imgList = contentNode.querySelectorAll('.ly-export-card');//需要转成图片的节点
  const cloneImgList = cloneContentNode.querySelectorAll('.ly-export-card');//克隆的,需要转成图片的节点
  const promisesTitle = Array.from(imgList).map((tit, index) => {
    return new Promise(res => {
     html2canvas(tit).then(canvas => {
        let url = canvas.toDataURL('image/png', 1); //这里拿到base64
        let img = new Image();
        img.src = url; 
        // 插入到clone的dom节点前面
        cloneImgList[index].parentNode.insertBefore(img, cloneImgList[index]);
        // 删除原有dom
        cloneImgList[index].parentNode.removeChild(cloneImgList[index]);
        res();
      });
    });
  });
  // 将dom副本传入插件,生成文件对象,并下载下来
  return Promise.all(promisesTitle)
    .then(() => {
    let template = `
    <!DOCTYPE html>
    <html lang="en">
    ${document.head.outerHTML}
    <body>
    ${cloneContentNode.outerHTML}
    </body>
    </html>`;
    const converted = htmlDocx.asBlob(template);
    saveAs(converted, `${fileName}.docx`);
  })
    .catch(e => {
    console.log(e);
  });
}

// 下载文件
export function saveAs(blob, fileName) {
  const a = document.createElement('a');
  const url = URL.createObjectURL(blob);
  
  a.href = url;
  a.download = fileName;
  a.display = 'none';
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
}

😰 由于业务需求,这个页面有很多图表都需要转成图片,以上的写法耗时比较长,在下载过程中页面造成卡顿。于是用console.time 找出耗时的地方是 html2canvas 转成canvas的时候。

😵 看了官方文档的配置,也没有看到有加快速度的方法。


于是,我考虑再换一个插件。最后找到了dom-to-image。

换成这个插件后,导出速度从之前的13秒,缩短到3秒左右!心情瞬间愉悦了呀😆

然后立刻在github上给 dom-to-image 点了一个 ⭐

当然换成 dom-to-image 的时候也遇到了小问题,但都比较容易解决了。

比如:

1、下载的图片背景是透明的

解决方法:给要生成图片的dom节点css样式设置背景颜色为白色。或者通过domtoimage.toPng(tit,options)的options参数设置图片背景

通过options还可以调整图片的大小画质 等。详细请看 官方文档

2、图片内容错位,有一些图片只显示了一半

解决方法:要生成图片的dom节点不要设置margin

📄 导出word第二代代码(dom-to-image+ htmlDocx )

import htmlDocx from 'html-docx-js/dist/html-docx';
import domtoimage from 'dom-to-image';

export function wordExport(id, fileName) {
  let contentNode = document.getElementById(id);
  
  // dom转成canvas,再转成图片
  let cloneContentNode = contentNode.cloneNode(true);//克隆dom节点
  const imgList = contentNode.querySelectorAll('.ly-export-card');//需要转成图片的节点
  const cloneImgList = cloneContentNode.querySelectorAll('.ly-export-card');//克隆的,需要转成图片的节点
  const promisesTitle = Array.from(imgList).map((tit, index) => {
    return new Promise(res => {
     domtoimage.toPng(tit).then(function(url) {
        let img = new Image();
        res();
        img.src = url;
        // 插入到clone的dom节点前面
        cloneImgList[index].parentNode.insertBefore(img, cloneImgList[index]);
        // 删除原有dom
        cloneImgList[index].parentNode.removeChild(cloneImgList[index]);
      });
    });
  });
  // 将dom副本传入插件,生成文件对象,并下载下来
  return Promise.all(promisesTitle)
    .then(() => {
    let template = `
    <!DOCTYPE html>
    <html lang="en">
    ${document.head.outerHTML}
    <body>
    ${cloneContentNode.outerHTML}
    </body>
    </html>`;
    const converted = htmlDocx.asBlob(template);
    saveAs(converted, `${fileName}.docx`);
  })
    .catch(e => {
    console.log(e);
  });
}

另外,实现了以下的功能:

  1. 章节导航:需要导航的文本,用标题标签h1-h6
  2. 文档的换行:如果两个图片或者文本要换行,可用

<span>&nbsp;</span> 或者 <p>&nbsp;</p>

❤️ 更多前端知识欢迎关注公众号交流

qrcode_for_gh_002ca2bfa5b1_258 (1).jpg