距离上一篇文章过去了二十多天了,期间一直想把第二部分写完,结果在测试过程中遇到了各种坑爹的问题,到今天才算基本完成,也许还有后续,但趁着今天有时间就写出来吧,也算对这个项目的一个总结了

遇到最大问题:

项目的需求是在一个窗口里生成所有图表,还要考虑到整套打印,所以滚动加载和分页浏览不是最好的方案,这导致数据超级多的时候(大概会生成2000多页的报告且上不封顶),会造成页面假死,疯狂占用电脑内存,低配置的电脑根本无法加载,甚至造成死机

在项目结构上我们采用数据分发的方式控制组件的渲染,由大致小每层组件都对数据进行过滤,重新组成新的数据传递给下一级,根据数据去判断显示与否,由于vue里v-if的机制如果该模块数据不存在,那么组件将不被渲染

一般来说我解决问题只有两种方式,一是找到解决问题的办法,二是让这个问题彻底消失,显然第二个是在这是行不通的,所以先分析原因:

1.后端返回的是原始数据,大量代码都需要前端进行处理,在前端进行如此大工作量的数据处理,显然内存消耗也是巨大的,显然这是不明智的,但后台数据暂时无法做进一步处理
2.echarts绘制图表的同时动画和频繁操作dom添加canvas也是也是消耗性能的元凶之一
3.大量的图表绘制同步进行会导致阻塞

原因已经找到接下来就是解决问题

先说动画的问题,这个在echarts的api里已经提出的解决办法,有两种,我这里都用到了:

1.全部图表绘制都有动画渲染的情况

2.单个图表显示超多数据的情况

第一个可以对echarts对象设置animate属性来关闭所有动画

animate:false

第二个需要设置progressive属性

progressive属性的作用如下:

渐进式渲染时每一帧绘制图形数量,设为 0 时不启用渐进式渲染,支持每个系列单独配置。

在图中有数千图形甚至好几万图形的时候,一下子把图形绘制出来,或者交互重绘的时候可能会造成界面的卡顿甚至假死,因此 ECharts 从 3.2.0 开始支持大量图形的渐进式渲染(progressive rendering),渲染的时候会把创建好的图形分到数帧中渲染,每一帧渲染只渲染指定数量的图形。

该配置项就是用于配置该系列每一帧渲染的图形数,默认是 400 个,可以根据图表图形复杂度的需要适当调整这个数字使得在不影响交互流畅性的前提下达到绘制速度的最大化。
比如在 lines 图或者平行坐标中线宽大于 1 的 polyline 绘制会很慢,这个数字就可以设置小一点,而线宽小于等于 1 的 polyline 绘制非常快,该配置项就可以相对调得比较大。

再说频繁操作dom导致的卡顿问题

首先感谢老大提供的的思路,这个问题可以和同步绘制一起来解决,在这里需要仔细的研究一下同步异步的问题,这个问题想清楚了,问题就解决了

在这里推荐阮一峰老师的JavaScript 运行机制详解:再谈Event Loop

JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。这个时候问题就出现了,当我在处理完数据传给图表的执行方法的时候我是这么写的:

var data = 处理好的数据;
for(var i=0;i<data.length;i++){
chart({id:'xxxx'+i,data:data[i]});

}

这条被循环执行的数据多的有可能是上千条,而且这还只是其中一个模块的数据,这样的话就是上千条的数据在主线程上排队,一个图表必须要等到上一个图表绘制完毕才会绘制下一个,并且在这个时候我其他的操作都是在等待图表绘制完成的,也就是说必须要等到所有图表绘制完毕,所有页面加载出来我才能去计算页码并将其赋值,这个期间目录页的大模块页码定位全都是空白的,而这时候由于要等待所有操作完成,且cpu这时候被占满,自然而然的就造成了页面的假死状态.既然同步渲染会造成假死,那么解决方案自然就有了:异步执行绘制图表方法

先看一下异步的运行机制

(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。

首先先确定哪些任务是要在主线程内执行的

数据的处理

组件的渲染(不包含图表)

页码的赋值

目录页的定位

这些主线程的任务都是可以同步进行的,且速度非常快,这样就避免了必须要等待所有图表渲染完成才能确定页码的尴尬

接下来是异步队列

通过es6的promise方法可以很轻松的实现图表异步的执行,关于promise大家也可以自行百度,在这里不出详细解释,不明白的同学暂时只需要知道这是一种js的异步变成解决方案就可以了;

打个比方,你有一个执行柱状图的方法,还有n个需要绘制柱状图的模块,在这里的解决方案是:

1.新建一个公共的图表执行方法的js文件,将所有图表方法都放在一起,然后按需引入

图表作为一个对象有两个字段:data和method

export const Chart = {
data:[],
method:function(obj){
  //这里放绘制图表的方法
  }
};

注意这个data,他就相当于一个任务队列,当我处理完数据时,不是第一时间就去执行绘制的方法,而是将处理好需要图表渲染的数据添加到这个data的队列里,每一个用到该图表的模块都是如此,这样一来等数据处理结束data队列里就存着所有需要渲染的数据了,

这个时候组件照常渲染,页码照常出,不去渲染图表,卡顿假死的问题就解决了,虽然还没有图表,但是起码页面已经加载出来了,接下来要做的就是去将队列里的数据进行异步的执行了

最开始考虑过使用定时器延时去传递数据加载图表,像下面这样

for(var i=0;i<10000;i++){
setTimeout(function(){
chart(data[i])
},1000*i)
}

其实这样也是可行的,每一个图表的渲染都延迟执行一秒,定时器其实也算是异步执行了,当所有的主线程走完再去执行定时器的方法,但这样的话相当于有10000个定时器在等待执行啊,虽然相隔一秒,不会造成卡顿,但显然不是最优方案,

所以最终使用的是 promise的方法,这样就变成了只有一个定时器,代码如下

// 异步执行图表
export const parmise = obj =>{
console.log('数据加载完毕,准备生成图表');
var p1 = new Promise(function(resolve){
resolve(0);
});
p1.then(fun1);
function continueFunc(_index){
var p2 = new Promise(function(resolve){
resolve(_index);
});
p2.then(fun1);
}
function fun1(ind){
obj.chart(obj.arr[ind]);
setTimeout(function(){
if(ind!=obj.arr.length-1){
ind++;
continueFunc(ind);
}
},500);
};
obj.chart(); obj.parevArrLen=obj.arr.length; };

vue的munted方法代表的是所有页面加载完成再去执行,在app.vue里把promise放在这里在合适不过了,当页面渲染完成异步执行图表绘制的方法,最大程度的解决卡顿问题

//先引入
import { parmise,chart } from './assets/js/chart.js'
//在mounted里执行
parmise(chart);

ok,到这里问题解决,基本上每次滑动滚轮时图表绘制两个左右,出图速度飞快,低配置机器也可正常运行;

最后接着上一篇的打印报告来说,因为之前试验过使用HTMLtopPDF打印,所以在写项目期间就没有进行过测试,当项目完成调试打印的时候才发现由于是多页面应用所以根本无法打印,由于HTMLtopPDF是后端的解决方案,我们在前端也不好调试,所以选择了前端打印pdf的方案,

查了许多资料后决定使用html2canvas 和 jsPDF结合使用来生成pdf

html2canvas : 通过遍历页面DOM结构,收集所有元素信息及相应样式,渲染出canvas image

jsPDF:可以通过文字和图片生成pdf

看了他们的作用相信观众老爷们也知道要怎么结合使用了,很简单在点击下载按钮时通过html2canvas将页面转换为canvas image然后通过jsPDF再进行pdf转换就ok了,接下来上简单的教程;

html2canvas

我们可以直接在浏览器端使用html2canvas,对整个或局部页面进行‘截图’。但这并不是真的截图,而是通过遍历页面DOM结构,收集所有元素信息及相应样式,渲染出canvas image。

由于html2canvas只能将它能处理的生成canvas image,因此渲染出来的结果并不是100%与原来一致。但它不需要服务器参与,整个图片都由客户端浏览器生成,使用很方便。

使用

使用的API也很简洁,下面代码可以将某个元素渲染成canvas:

html2canvas(element, {
onrendered: function(canvas) {
// canvas is the final rendered <canvas> element
}
});

通过onrendered方法,可以将生成的canvas进行回调,比如插入到页面中:

html2canvas(element, {
onrendered: function(canvas) {
document.body.appendChild(canvas);
}
});

做个小例子代码如下

<html>
<head>
<title>html2canvas example</title>
<style type="text/css">...</style>
</head>
<body>
<header>
<nav>
<ul>
<li>one</li>
...
</ul>
</nav>
</header>
<section>
<aside>
<h3>it is a title</h3>
<a href="">Stone Giant</a>
...
</aside>
<article>
<img src="./Stone.png">
<h2>Stone Giant</h2>
<p>Coming ... </p>
<p>以一团石头...</p>
</article>
</section>
<footer>write by linwalker @2017</footer>
<script type="text/javascript" src="./html2canvas.js"></script>
<script type="text/javascript">
html2canvas(document.body, {
onrendered:function(canvas) {
document.body.appendChild(canvas)
}
})
</script>
</body>
</html>

这个例子将页面body中的元素渲染成canvas,并插入到body中

jsPDF

jsPDF库可以用于浏览器端生成PDF。

文字生成PDF

使用方法如下:

// 默认a4大小,竖直方向,mm单位的PDF
var doc = new jsPDF(); // 添加文本‘Download PDF’
doc.text('Download PDF!', 10, 10);
doc.save('a4.pdf');

图片生成PDF

使用方法如下:

// 三个参数,第一个方向,第二个单位,第三个尺寸格式
var doc = new jsPDF('landscape','pt',[205, 115]) // 将图片转化为dataUrl
var imageData = ‘data:image/png;base64,iVBORw0KGgo...’; doc.addImage(imageData, 'PNG', 0, 0, 205, 115);
doc.save('a4.pdf');

文字与图片生成PDF

// 三个参数,第一个方向,第二个尺寸,第三个尺寸格式
var doc = new jsPDF('landscape','pt',[205, 155]) // 将图片转化为dataUrl
var imageData = ‘data:image/png;base64,iVBORw0KGgo...’; //设置字体大小
doc.setFontSize(20); //10,20这两参数控制文字距离左边,与上边的距离
doc.text('Stone', 10, 20); // 0, 40, 控制文字距离左边,与上边的距离
doc.addImage(imageData, 'PNG', 0, 40, 205, 115);
doc.save('a4.pdf')

生成pdf需要把转化的元素添加到jsPDF实例中,也有添加html的功能,但某些元素无法生成在pdf中,因此可以使用html2canvas + jsPDF的方式将页面转成pdf。通过html2canvas将遍历页面元素,并渲染生成canvas,然后将canvas图片格式添加到jsPDF实例,生成pdf。

html2canvas + jsPDF

单页

将demo1的例子修改下:

<script type="text/javascript" src="./js/jsPdf.debug.js"></script>
<script type="text/javascript">
var downPdf = document.getElementById("renderPdf");
downPdf.onclick = function() {
html2canvas(document.body, {
onrendered:function(canvas) { //返回图片dataURL,参数:图片格式和清晰度(0-1)
var pageData = canvas.toDataURL('image/jpeg', 1.0); //方向默认竖直,尺寸ponits,格式a4[595.28,841.89]
var pdf = new jsPDF('', 'pt', 'a4'); //addImage后两个参数控制添加图片的尺寸,此处将页面高度按照a4纸宽高比列进行压缩
pdf.addImage(pageData, 'JPEG', 0, 0, 595.28, 592.28/canvas.width * canvas.height ); pdf.save('stone.pdf'); }
})
}
</script>

关于打印大概就写这些吧,详细的教程大家可以去自行百度超多的;

基于Vue.js的大型报告页项目实现过程及问题总结(二)的更多相关文章

  1. 基于Vue.js的大型报告页项目实现过程及问题总结(一)

    今年5月份的时候做了一个测评报告项目,需要在网页正常显示的同时且可打印为pdf,当时的技术方案采用jquery+template的方式,因为是固定模板所以并没有考虑报告的模块化区分,九月底产品提出新的 ...

  2. 基于animate.css动画库的全屏滚动小插件,适用于vue.js(移动端、pc)项目

    功能简介 基于animate.css动画库的全屏滚动,适用于vue.js(移动端.pc)项目. 安装 npm install vue-animate-fullpage --save 使用 main.j ...

  3. Vue项目中使用基于Vue.js的移动组件库cube-ui

    cube-ui 是滴滴公司的技术团队基于 Vue.js 实现的精致移动端组件库.很赞,基本场景是够用了,感谢开源!感谢默默奉献的你们. 刚爬完坑,就来总结啦!!希望对需要的朋友有小小的帮助. (一)创 ...

  4. 新建一个基于vue.js+Mint UI的项目

    上篇文章里面讲到如何新建一个基于vue,js的项目(详细文章请戳用Vue创建一个新的项目). 该项目如果需要组件等都需要自己去写,今天就学习一下如何新建一个基于vue.js+Mint UI的项目,直接 ...

  5. 基于 Vue.js 之 iView UI 框架非工程化实践记要 使用 Newtonsoft.Json 操作 JSON 字符串 基于.net core实现项目自动编译、并生成nuget包 webpack + vue 在dev和production模式下的小小区别 这样入门asp.net core 之 静态文件 这样入门asp.net core,如何

    基于 Vue.js 之 iView UI 框架非工程化实践记要   像我们平日里做惯了 Java 或者 .NET 这种后端程序员,对于前端的认识还常常停留在 jQuery 时代,包括其插件在需要时就引 ...

  6. 「免费开源」基于Vue和Quasar的前端SPA项目crudapi后台管理系统实战之自定义组件(四)

    基于Vue和Quasar的前端SPA项目实战之序列号(四) 回顾 通过上一篇文章 基于Vue和Quasar的前端SPA项目实战之布局菜单(三)的介绍,我们已经完成了布局菜单,本文主要介绍序列号功能的实 ...

  7. 基于VUE.JS的移动端框架Mint UI

    Mint UI GitHub:github.com/ElemeFE/mint 项目主页:mint-ui.github.io/# Demo:elemefe.github.io/mint- 文档:mint ...

  8. 基于Vue、web3的以太坊项目开发及交易内幕初探 错误解决总结

    基于Vue.web3的以太坊项目开发及交易内幕初探 本文通过宏观和微观两个层面窥探以太坊底层执行逻辑. 宏观层面描述创建并运行一个小型带钱包的发币APP的过程,微观层面是顺藤摸瓜从http api深入 ...

  9. [译]基于Vue.js的10个最佳UI框架,用于构建移动应用程序

    原文查看10 Best Vue.js based UI Frameworks for Building Mobile Apps 如果您期待使用Vue.js构建移动应用程序,那么您可以选择许多可用的UI ...

随机推荐

  1. CSS样式设置语法全解,样式优先级、值和单位、字体、文本、块级元素,行内元素,替换元素、非替换元素、display、float、position、table、li、光标、边距边框、轮廓、颜色背景

    全栈工程师开发手册 (作者:栾鹏) 一个demo学会css css选择器全解 css操作语法全解 CSS样式设置语法全解: 样式优先级 1. !important标记的样式 > 内联样式(sty ...

  2. 对象反序列化时,抛出java.io.StreamCorruptedException: invalid type code: AC异常

    问题描述:在使用java.io.ObjectInputStream类的readObject()方法去读取包含有序列化了多个(两个及两个以上)类的文件时,当读取到第二个类时,会抛出题目中提到的异常. 原 ...

  3. rewrap-ajax.js插件

    很久没有动手写技术的文章,这个过程中间一直在写日记,生活点滴的记录替代了技术文章的编写,可以看出以往的内心是激情或烈火,现在是... 最近写了一个JS插件,用圈内的话说叫造了个轮子,造的好与不好都不是 ...

  4. C#使用互斥量(Mutex)实现多进程并发操作时进程间的同步操作(进程同步)

    本文主要是实现操作系统级别的进程同步的代码及测试结果,代码经过测试,可直接使用,也可供参考. 承接上一篇博客的业务场景[C#使用读写锁三行代码简单解决多线程并发写入文件时线程同步的问题]. 随着服务进 ...

  5. Prometheus 到底 NB 在哪里?- 每天5分钟玩转 Docker 容器技术(84)

    本节讨论 Prometheus 的核心,多维数据模型.我们先来看一个例子. 比如要监控容器 webapp1 的内存使用情况,最传统和典型的方法是定义一个指标 container_memory_usag ...

  6. 将摄像头的读入的人像放入背景视频中_with_OpenCV_in_Python

    import cv2 import numpy as np import time cap = cv2.VideoCapture(0) background_capture = cv2.VideoCa ...

  7. SimpleDateFormat时间格式化存在线程安全问题

    想必大家对SimpleDateFormat并不陌生.SimpleDateFormat 是 Java 中一个非常常用的类,该类用来对日期字符串进行解析和格式化输出,但如果使用不小心会导致非常微妙和难以调 ...

  8. MapReduce-实践1

      MR进阶实践1:  -file 分发多个文件 [-file 适合场景]分发文件在本地,小文件 -file分发原理         run.sh文件: 通过多个-file, 将多个本地文件分发到Ha ...

  9. 网络基础二 tcp/ip协议簇 端口 三次握手 四次挥手 11种状态集

    第1章 概念介绍 1.1 VLAN 1.1.1 什么是VLAN VLAN(Virtual LAN),翻译成中文是“虚拟局域网”.LAN可以是由少数几台家用计算机构成的网络,也可以是数以百计的计算机构成 ...

  10. 使用Lock锁生产者消费者模式

    package com.java.concurrent; import java.util.concurrent.locks.Condition; import java.util.concurren ...