背景:專案需求中要在頁面上渲染大約50萬條左右的波形資料圖表
那麼如何解決渲染中的卡頓呢?
肯定是要從伺服器端和前端一起優化這是毋庸置疑的。
1.伺服器端:
伺服器端耗時最多的一定是在資料庫的篩選資料的行為上,本次需求中資料的篩選是根據物理量的型別和時間來進行的。
為了提速,應當取消掉其他的篩選條件,並且使用mongodb和redis,還應該將資料分片傳送給前端。
2.前端:
首先我們要搞清楚,優化策略的重點是在資料的拿取上,因為渲染的速度其實遠快於資料互動的速度,要想提速首先要解決的是短板。
在資料拿取時我們應當進行輪詢,分片的拿到伺服器端傳輸的資料,然後進行渲染。
我們來整理一下思路:
1.第一次輪詢結束拿到資料後,我們需要進行繪圖。然後判斷是否進行下一次輪詢
2.第二次輪詢結束之後我們需要將拿到的資料,append到圖表之中,然後判斷否進行下一次輪詢
3.如何隨時的讓輪詢終止。
這三個就是目前我們需要解決的問題點。
第一次拿到資料之後我們判斷資料的長度是否為0,為0則終止輪詢,不為0則繼續。
後面繼續輪詢時,每次輪詢拿到資料都要判斷圖表是否存在,存在就dispose,然後重繪。
要注意的點時,我們的圖表是可以縮放的,所以在重繪時還需要將縮放條的位置進行記錄,然後設定到datazoom裡面,這樣可以提高使用者體驗。
下面貼出程式碼:
getListWaveformDat(count) { this.loading = true;//載入loading動畫 //獲取波形圖資料 getListWaveformDat({ deviceId: this.queryPointParams.deviceId, diId: this.diId, reportedOn: this.orgTime, keyName: this.dataAxis, num: this.pageNum, size: 10000, count: count ? count : '', }).then((res) => { if (res.length > 0) { this.noData = false//是否載入預設值圖片 console.log(this.orgchart) if (this.orgchart) { this.orgchart.dispose(); } this.oscillograph = res; let x = []; for (let i = 0; i < this.oscillograph.length; i++) { x[i] = this.oscillograph[i].count; }//處理X軸資料 let y = []; for (let i = 0; i < this.oscillograph.length; i++) { y[this.oscillograph[i].count * 1 - 1] = this.oscillograph[i].value * 1 } for (let i = 0; i < this.oscillographY.length; i++) { if (this.oscillographY[i] == undefined) { if (y[i]) { this.oscillographY[i] = y[i] } } }//處理Y軸資料 console.log(this.oscillographY) this.pageNum = this.pageNum + 1;//輪詢次數加1 this.$nextTick(() => { this.orgDraw();//繪製圖表 }) this.loading = false;//關閉載入loading this.getListWaveformDat(x[x.length - 1])//繼續輪詢 } else { //如果載入的資料為空 this.loading = false; console.log(this.orgchart) if (this.pageNum == 1) { //如果第一次輪詢就為空,載入預設圖片 this.noData = true; if (this.orgchart) { this.orgchart.dispose();//清除上一次載入的圖表 } this.pageNum = 1;//請求完所有資料之後初始化一下 return } }); },
這是介面返回的資料來源,X就是count,Y就是Value。因為每次輪詢查到的資料都是亂序的,但是圖表要求X,Y必須對應所以需要對資料進行重新排序。
思路:1.先獲取X軸的長度,然後根據長度生成X,Y兩個陣列。2.將Y陣列的值都設定為undefined,X陣列的值設為1-X的長度3.遍歷介面的資料,將count作為Y的索引,將value塞入對應的元素中。
getX() { getMaxCount( { deviceId: this.queryPointParams.deviceId, reportedOn: this.orgTime, keyName: this.dataAxis, } ).then((res) => { console.log(res, '======') this.oscillographX = Array.from({ length: res * 1 }, (value, key) => key + 1) this.oscillographY = Array.from({ length: res * 1 }, (value, key) => undefined) console.log(this.oscillographX); }) },
處理X,Y軸資料的程式碼在第一個程式碼塊中已經有就不貼了。
完成資料處理之後就是進行繪圖。
orgDraw() { let that = this; if (this.orgchart) { this.orgchart.dispose(); } console.log(this.start, this.end, 'xxx') if (this.tabname !== "原始資料") { return; } // if (this.orgchart) { // this.orgchart.dispose() // } var chartDom = document.getElementById("orgChart"); var myChart = echarts.init(chartDom); const option = { title: { left: "center", text: "原始資料", }, tooltip: { trigger: "axis", axisPointer: { type: "shadow", }, }, grid: { bottom: 90, }, dataZoom: [{ type: 'inside',//圖表下方的伸縮條 show: true, //是否顯示 realtime: true, //拖動時,是否實時更新系列的檢視 start: this.start, //伸縮條開始位置(1-100),可以隨時更改 end: this.end, //伸縮條結束位置(1-100),可以隨時更改 }, { type: 'slider',//圖表下方的伸縮條 show: true, //是否顯示 realtime: true, //拖動時,是否實時更新系列的檢視 start: this.start, //伸縮條開始位置(1-100),可以隨時更改 end: this.end, //伸縮條結束位置(1-100),可以隨時更改 } ], xAxis: { data: this.oscillographX, silent: false, splitLine: { show: false, }, splitArea: { show: false, }, }, yAxis: { }, series: [ { // seriesIndex: 9, type: "line", data: this.oscillographY, large: true, }, ], }; console.log(myChart.appendData) myChart.setOption(option, true); // myChart.appendData({ // seriesIndex: 0, // data: this.oscillographY // }) myChart.on('datazoom', function (params) { // let xAxis = myChart.getModel().option.xAxis[1];//獲取axis console.log(params.batch[0].end, params.batch[0].start, 'xAxis') that.start = params.batch[0].start; that.end = params.batch[0].end; });//記錄datazoom的捲動距離 this.orgchart = myChart; this.isStart = false; return; },
繪圖中唯一需要做的就是記錄datazoom的捲動進度拿到start和end重繪之後進行賦值。
總結一下:處理的思路就行以一萬條資料為一次不斷進行輪詢,將資料不斷的拼接,然後重新繪圖。為什麼不用echarts提供的appendData()方法呢?因為根本不支援。