前面我曾经写过一个局部打印的组件,但其实一直没有将其实际应用。这次公司要将项目部署出去了,然后我在自测时才发现打包后的表格样式失效了!
然后就是漫漫的找Bug过程了,找了有三四个小时,总算是一点点一点点理清了头绪,下面我来讲讲怎么解决打包后样式失效的问题。没有看过我上一篇文章的可以先去看一下。传送门
兼容性问题?
刚发现这个问题时,我内心是实在不愿意接受的,毕竟也是花了那么长时间去钻研这个东西了,而且平时测试的时候也并没有出现什么问题呀。
所以内心日常甩锅给兼容问题,我测了一下在tomcat服务器和nginx服务器下的表现,发现一毛一样。又猜想是浏览器问题,又在edge上试了一下,也不对。最后发现,只有开发模式和生产模式两种构建方式的不同导致了最后结果的不同。
到了这里我其实是很疑惑的,平时开发中一般都是生产模式的样式和行为都会和开发模式中一毛一样的啊,Vue怎么会让我操心这种问题呢?
然后我找到执行打印功能部分的代码,发现会在生成的iframe中导入当前页面的样式,我把最后生成的样式部分打印了出来,然后观察两种模式的不同,结果真让我发现了问题:生产模式中样式全都是link标签,而开发模式中样式都被包裹在style标签中。
总结:开发模式中,由于我们直接编辑的
.vue
文件,vue直接将.vue
文件中的style标签部分解析出来放到body中,而生产模式中,打包之后的文件只有标准的HTML、CSS和JS文件,也就只能用link标签引入外部CSS文件了。
这样的不同直接就导致了我那种写法在发送打印指令时页面还没有把CSS文件加载到的问题,如下图:
弹出对话框时
对话框关闭后
在打印对话框出来时,刚刚开始请求CSS文件,此时页面上自然就不会有样式被渲染出来。在开发模式行得通的原因就是style是内嵌在页面里的,就可以直接渲染出来。
知道了问题的原因,那就好解决了,我们在iframe的onload事件中执行打印操作即可。优化后的代码如下:
printTable () {
// 1.获取要打印的内容的一份复制,否则待会儿添加节点时会将原有内容删除
const content = document.getElementById('printTable').cloneNode(true)
// 2.获取放置打印内容的iframe
const ifm = document.getElementById('printf')
// css会重新加载 这里监听iframe的加载事件 完成后再执行打印功能
ifm.onload = () => {
// 4.添加打印内容并打印
ifm.contentDocument.body.appendChild(content)
ifm.contentWindow.print()
}
// 3.添加打印内容样式
const styles = document.querySelectorAll('style,link')
let str = ''
for (let i = 0; i < styles.length; i++) {
// 排除其他类型的内容,如ico js
if ((styles[i].tagName === 'LINK' && styles[i].rel === 'stylesheet') || styles[i].tagName === 'STYLE') {
str += styles[i].outerHTML
}
}
// 向iframe添加内容 注:如果没有则页面的加载事件不会触发
ifm.contentDocument.open()
ifm.contentDocument.write(str)
ifm.contentDocument.write('<div></div>')
ifm.contentDocument.close()
},
这样之后,就可以愉快地开始打印啦
评论区