关于vue-print-nb这个库的一些思考
前言
打印是前端挺常见的一个需求,整个页面打印只需要执行 window.print()
即可。而页面的局部打印就比较麻烦了,需要借助iframe实现。我目前在 github
上找到比较好用的vue相关的是 vue-print-nb 这个库。公司内部也是使用的这个,但是最近在开发一个需求的时候在chrome49上遇到一个bug,引发了一些我思考。
注:下面说到的bug仅在chrome49上出现,高版本没有问题。
遇到的bug
首先交代一下遇到了什么bug吧。我在开发一个报告弹窗相关的组件,其中需要实现打印报告的功能,使用的就是 vue-print-nb
这个库。之前其实也使用过很多次,一直都没啥问题,但是这次却出现了无法弹出打印窗口的问题。我一开始以为是我封装组件的问题,因为在同样的开发环境下,之前开发的功能可以打印,而新的组件无法打印。但是经过调试后发现其实是已经执行到 vue-print-nb
的库的代码,具体如下图:
与之前可以打印功能不同的是,这个组件的打印执行了 print
函数后,并没有执行 load
事件的回调,所以也就根本没有执行 iframeWin.print()
。
那么为什么同样的开发环境,同样的chrome49,之前开发的功能是正常的,而这次开发的组件就无法打印了,我仔细去阅读了 vue-print-nb
相关的源码,并产生了一些疑问。
产生的疑问
-
在
document.body.appendChild(iframe)
后才对iframe
进行load
事件的绑定会不会产生问题?源码中在
document.body.appendChild(iframe)
后又执行了很多代码,最后才进行的load
事件的绑定(感兴趣的小伙伴可以自行阅读源码,这里就不多做介绍了); 其实大部分情况都是不会有问题的,因为load
事件是需要等待iframe
中内容全部加载完成才会执行,尤其是一些图片等加载是比较慢的,而代码的执行速度是很快的。 -
为什么在
load
事件时才执行打印,而不是直接执行打印看源码可知
document.body.appendChild(iframe)
执行的很早,那为什么还要把iframeWin.print()
放到load
回调函数内执行,而不是直接执行呢?是因为需要保证iframe
内容(尤其是图片)加载完成再在执行打印,否则可能会出现打印窗口内图片无法显示或者只显示一半的情况。 -
为什么不在
print
函数中load
事件绑定后执行document.body.appendChild(iframe)
?因为仅通过
createElement
创建出来的iframe
是不具有浏览上下文(browsing context)的,也就是没有iframe.contentWindow
和iframe.contentDocument
,那也就意味着无法通过iframe.contentDocument.write
方法将需要打印的内容写入到iframe了。
产生bug的原因
说了那么多,那到底是什么原因产生了这个bug呢?请回看上述疑问第一条中提到:大部分情况都是不会有问题的;那么什么情况下会有问题呢?就是当打印内容很少或者全部都是文本时,iframe
很快就能够 load
完成,那么等到 print
函数中执行 load
事件绑定时,其实 iframe
已经 load
完成,这就会产生无法弹出打印窗口的bug(即没有执行 iframeWin.print()
)。
解决方案
解决这个问题的思路就是在 print
函数中做判断,判断如果 iframe
已经 load
完成,那么我们应该直接执行 _loaded
函数;如果 iframe
还没 load
完成那么再进行 load
事件的绑定。那么该如何判断 iframe
是否已经 load
完成呢?思路就是在 document.body.appendChild(iframe)
前就先绑定一个 load
事件回调,伪代码如下:
let iframeLoaded = false
// ...
iframe.addEventListener('load', () => {
iframeLoaded = true
})
document.body.appendChild(iframe)
// ...
// print函数内
// ...
const _loaded =() => {
// ...
iframeWin.print()
iframeLoaded = true
}
if (iframeLoaded) {
_loaded()
} else {
iframe.addEventListener('load', _loaded)
}
总结
本文主要讲述了发现bug、阅读源码、分析原因、提出解决方案的一系列思路。同时也没发现关于打印的库比较少,我自己也准备着手写一个比较完善、支持ts的打印库。
转载自:https://juejin.cn/post/7177392882884542525