likes
comments
collection
share

使用jsPdf 将页面生成PDF文件并自动上传 DEMO(三)

作者站长头像
站长
· 阅读数 72

需求背景

在项目中有这样一个需求,需要调用其他系统业务API生成页面(canvas)渲染图表,并将图表导出成PDF文件,上传到服务器,并将文件地址,传给第三方业务系统,使第三方业务系统能够通过文件服务器下载该图表PDF文件

具体分析

经过上一篇 《使用jsPdf 将页面生成PDF文件并自动上传 DEMO(二)》,我们已经能够正确的将页面图表转成pdf 并上传至文件服务器,将结果同步给第三方了

今天我们就来解决剩下的问题 如何保证很多个图表渲染的时候页面不卡顿???

经过我们简单的分析 页面卡顿甚至崩溃的根本原因是 页面上渲染了太多了图表,也就是 前文 提到的com-a

因为我们是使用v-for进行渲染生产的图表,那么随着 list 长度的增加 渲染的dom 就会越来越多,页面就会越来越卡,直至浏览器崩溃!

如何解决这一问题呢?

既然 v-for 这个方式存在问题,我们就换个思路,我们一个一个的生成使用组件复用的方式,确保 dom 树中 始终 只有 一份 com-a 的dom, 由最开始的渲染 N个 变成现在的渲染 1 个

为了以这样的方式实现功能,我们需要维护一个队列,去递归消耗这个队列,直到队列被清空,然后 缓存的 canvas 列表,转成 PDF 文件,剩下的就是上传及后续的操作了。

那么 我们来实现一下 这个功能:

参考代码 (较完整)

<template>
    <com-a ref="comA" style="dispaly:none;" />
</template>

<script>
export default {
    data(){
        return {
            pageInfo:[], // 存放原始数据
            dataList:[], // 存放原始数据的拷贝
            imageCacheList:[], // 生成的image (canvas) 存放列表  
            timer:null, // 定时任务的 timer
        }
    },
    mounted(){
        
        this.dataList = JOSN.parse(JSON.stringify(this.pageInfo))
        
        this.timer = setTimeout(()=>{
            this.handleInit()
        },0)
        
    },
    methods:{
        handleInit(){
            if(this.dataList.length){
                const data = this.dataList.shift()
                this.handleRender(data)
            }else{
                if(this.cacheImageList.length){
                    this.handleImagesToPdf(this.cacheImageList,callback)
                }
            }
        },
        handleRender(data){
            const view = this.$refs.comA
            view && view.setData(data)
            
            // 由于这个地方不知道 canvas 到底什么时候能够渲染完成,
            // 我们使用定时器延时 1s 中后 获取canvas 
            // 在实际中,comA 会给我们提供一个 渲染完成的 hooks
            // 确保我们能够在正确的时机获取到的 canvas 是渲染完成的; 
            this.$nextTick(()=>{
                let timer = setTimeout(()=>{
                    this.handleCanvasToImage()
                },1000)
            })
            
        },
        handleCanvasToImage(){
            const canvas = this.$refs.comA.$el.querySelector('canvas')
            const image = cavas.toDataURL('image/jpeg',1,0)
            this.cacheImageList.push(image)
            
            // 回调 handleInit() 继续下个月 图表的渲染
            this.handleInit()
        },
        handleImagesToPdf(images,callback){
            const pdf = new jsPDF('p','pt','a4')
            const width  = pdf.internal.pageSize.getWidth()
            const height = pdf.internal.pageSize.getHeight()
            
            images.forEach((img,index)=>{
                pdf.addImage(img,'JPEG',0,0,width,heihgt)
                if(images.length - 1 !== index){
                    pdf.addPage()
                }
            })
            const filename = 'xxx.pdf'
            const blob = pdf.output('blob',{filename})
            const file = new File([blob],filename,{type:bolb.type})
            
            if(callback && typeof callback === 'function'){
                callback(file)
            }
            
        },
        handleUpload(file){
            const formData = new formData()
            formData.append('file',file)
            
            API.upload(formData).then((res)=>{
                // TODO 上传完成,重制某些参数,同步给第三方
                const fileId = res.data
                
                this.handleAsyncData()
                
            }).catch(()=>{
                console.log('上传失败')
            }).finally(()=>{
                this.handleRestParams()
            })
            
        },
        handleAsyncData(){
            // ....
        },
        handleRestParams(){
            this.imageCacheList = []
            this.dataList = []
            if(this.timer){
                clearTimeout(this.timer)
                this.timer = null
            }
        }
    },
    
}
</script>


截止目前 我们这个 demo 就基本已经完成了

下一步 我们将使用 函数的形式 调用 该组件,直接传递响应的参数,就可以完成剩下的操作;

也可以 emit 一些 事件,比如 upload 的 成功或失败,生成图表的进度等

例如 最终的调用方式 我们希望如下:

<button @click="handleCreatePDF({pageInfo:pageInfo})">{{process}}</button>

process: 
    生成PDF
    正生成第1张图表
    正生成第2张图表
    正生成第3张图表
    正生成第4张图表
    正在上传中...
    已上传成功/失败
    已同步第三方
    .....

最后的最后

这一路过来,我们发现在最基本的 domo 中,我们从最开始的基础demo的搭建,功能的验证,以及发现问题,解决问题,优化等,到最终的逐步成型;可以集成进入项目。到最后的 函数式调用,都是 逐步求精的过程。都是在 不断的发现问题,不断的取舍与权衡,不断的解决或规避问题,不断的优化细节;

到这结束了!

后面的以函数形式调用 就 不继续写文章了,感兴趣的同学可以 参考element ui$message 的实现 或者 参阅 vue 官方文档 extend 即可;

最后

欢迎围观:

使用jsPdf 将页面生成PDF文件并自动上传 DEMO(一)

使用jsPdf 将页面生成PDF文件并自动上传 DEMO(二)

最后的最后

有请出我们今天的主角,小趴菜

使用jsPdf 将页面生成PDF文件并自动上传 DEMO(三)

转载自:https://juejin.cn/post/7163065496282071077
评论
请登录