技術(shù)專欄 | DMap——實戰(zhàn)Vue百萬條數(shù)據(jù)渲染表格組件開發(fā)

作者:TalkingData 李志剛

本文由TalkingData原創(chuàng)悉盆,轉(zhuǎn)載請獲取授權(quán)鸟辅。

李志剛:近幾個月在開發(fā)一個基于Vue的數(shù)據(jù)可視化分析輔助應用———DMap(諦聽)倍奢,一套為數(shù)據(jù)分析師和數(shù)據(jù)科學家提供的基于位置大數(shù)據(jù)分析的工具熟嫩,旨在提高數(shù)據(jù)分析效率,降低獲取多數(shù)據(jù)并行分析成本机错,簡化大屏和數(shù)據(jù)報告開發(fā)制作流程爬范。其UI組件使用的是iView,地圖可視化庫使用的是inMap弱匪,服務端使用Node.js搭建青瀑。

DMap的核心就是服務大數(shù)據(jù)分析,所以當面對幾萬幾十萬甚至百萬級別的數(shù)據(jù)時痢法,性能優(yōu)化是一個具有挑戰(zhàn)性的問題狱窘。今天我就拿項目中一個表格渲染的優(yōu)化為例來展開介紹。

在前端開發(fā)中财搁,用表格來展示數(shù)據(jù)是再平常不過的了蘸炸,當數(shù)據(jù)量較多時,我們通常的做法是使用分頁尖奔,如果數(shù)據(jù)量不算太多只有兩三頁搭儒,我們大可以把全量數(shù)據(jù)獲取下來,在前端做簡單的分頁展示提茁。當數(shù)據(jù)量再上一個等級時淹禾,我們就需要根據(jù)頁數(shù)向服務端請求這一頁需要的數(shù)據(jù)。但是DMap作為助力大數(shù)據(jù)可視化的分析工具茴扁,我們需要將全量的數(shù)據(jù)在前端做展示铃岔,而為了提升用戶體驗,我們在表格的展示上決定不做分頁峭火,也不做懶加載毁习,而是像Excel那樣可以無縫隙的滾動。

在Web中卖丸,長列表渲染的性能問題已經(jīng)有一些成熟的方案纺且,表格和長列表相似,當渲染的行數(shù)達到一定量時稍浆,滾動就會變得卡頓载碌,所以我們使用了虛擬渲染的方案,就是只渲染用戶所能看到的區(qū)域的一小部分數(shù)據(jù)衅枫,然后通過滾動來計算顯示的數(shù)據(jù)嫁艇,和上下占位元素的高度。

通過這個圖可以對原理有個大概的了解为鳄,接下來說下計算上的細節(jié)裳仆。

首先我們需要監(jiān)聽表格外層容器(也就是顯示滾動條的元素)的scroll事件,在scroll事件綁定的方法中我們只做一件事孤钦,那就是獲取外層容器當前滾動了的高度scrollTop的值歧斟。我們的所有計算,包括三個表格位置的替換偏形、表格數(shù)據(jù)的選取静袖、上下占位元素的高度的計算都與scrollTop相關(guān)。

下面是scroll事件的綁定的方法:

handleScroll (e) {

constele = e.srcElement || e.target;

const{ scrollTop, scrollLeft } = ele;

this.scrollLeft = scrollLeft;

this.scrollTop = scrollTop;

}

我們只需要在這里把scrollTop和scrollLeft的值賦給vue實例對應的值俊扭,然后我們用watch監(jiān)聽scrollTop的改變队橙,如果更新了,就來計算當前處于可視區(qū)域的表格索引號currentIndex:

(注:左右滑動即可查看完整代碼萨惑,下同)

this.currentIndex = parseInt((top % (this.moduleHeight *3)) /this.moduleHeight);

這的top就是更新后的this.scrollTop的值捐康,moduleHeight是單個表格的高度,我們稱它為一個模塊庸蔼。

拿到currentIndex的值后解总,我們就可以計算三個表格的顯示位置,和每個表格中填充的數(shù)據(jù)姐仅。三個表格我們是通過render函數(shù)渲染的花枫,我們根據(jù)currentIndex的值來返回不同順序的render函數(shù):

getTables (h) {

let table1 =this.getItemTable(h,this.table1Data,1);

let table2 =this.getItemTable(h,this.table2Data,2);

let table3 =this.getItemTable(h,this.table3Data,3);

if(this.currentIndex ===0)return[table1, table2, table3];

elseif(this.currentIndex ===1)return[table2, table3, table1];

elsereturn[table3, table1, table2];

}

數(shù)組中表格順序不同,反應在頁面上的效果就是不同的先后順序掏膏。最后我們通過這個方法得到完整的render:

renderTable (h) {

returnh('div', {

style:this.tableWidthStyles

},this.getTables(h));

}

然后使用封裝的無狀態(tài)的組件劳翰,來渲染我們得到的表格render。

renderDom組件的實現(xiàn)如下:

exportdefault{

name:'RenderCell',

functional:true,

props: {

render:Function,

backValue: [Number,Object]

},

render:(h, ctx) =>{

returnctx.props.render(h, ctx.props.backValue, ctx.parent);

}

};

接下來我們講下三個表格中填充的數(shù)據(jù)的計算馒疹。

我們按照三個模塊都在可視區(qū)域經(jīng)過一次算是一輪佳簸,通過scrollTop來和currentIndex來計算每個模塊當前是在第幾輪展示,但因為我們是從第二個表格才開始做這個邏輯的處理(為了輪播效果更平滑)颖变,所以要先判斷當前滾動的高度是否大于一個模塊的高度生均,如果大于才做如下計算:

switch (this.currentIndex) {

case0: t0 = parseInt(scrollTop / (this.moduleHeight *3)); t1 = t2 = t0;break;

case1: t1 = parseInt((scrollTop -this.moduleHeight) / (this.moduleHeight *3)); t0 = t1 +1; t2 = t1;break;

case2: t2 = parseInt((scrollTop -this.moduleHeight *2) / (this.moduleHeight *3)); t0 = t1 = t2 +1;

}

計算出每個模塊在第幾輪展示后,就可以來取對應的表格數(shù)據(jù)了:

const count1 =this.times0 *this.itemNum *3;

this.table1Data =this.insideTableData.slice(count1, count1 +this.itemNum);

const count2 =this.times1 *this.itemNum *3;

this.table2Data =this.insideTableData.slice(count2 +this.itemNum, count2 +this.itemNum *2);

const count3 =this.times2 *this.itemNum *3;

this.table3Data =this.insideTableData.slice(count3 +this.itemNum *2, count3 +this.itemNum *3);

到這里虛擬渲染的重要內(nèi)容都介紹完了悼做。表格開發(fā)完成后疯特,在項目中實際使用時,當加載二十多萬條數(shù)據(jù)來測試時肛走,整個頁面卡的讓人無法忍受漓雅,數(shù)據(jù)量越大頁面卡頓越嚴重。我們的表格是沒有問題的朽色,問題出在Vue幫了我們“倒忙”邻吞。

在Vue實例中添加的對象,Vue會先遍歷一遍對象的所有屬性葫男,用——

Object.defineProperty()為每個對象創(chuàng)建對應的getter和setter抱冷。

而在項目中,我們的insideTableData只是一個數(shù)據(jù)集對象中的一個屬性梢褐,這個對象還包括很多與這一個數(shù)據(jù)集相關(guān)的信息旺遮,我們在使用this.insideTableData.slice獲取數(shù)據(jù)的時候會觸發(fā)this.insideTableData對應的getter赵讯,從而執(zhí)行一些其他邏輯,而我們的滾動又會頻繁的(僅當currentIndex變化的時候)需要重新填充表格數(shù)據(jù)耿眉,所以這會造成卡頓边翼。

解決這個問題的辦法就是阻止Vue給我們的數(shù)據(jù)集對象設置對應的setter和getter,

我了解的有兩種方法鸣剪,一是文檔中提到的:

我們使用的時候就需要通過——

this.$data._dataSet.insideTableData(這里的_dataSet就是一個數(shù)據(jù)集對象)來獲取组底。

另一種方法,就是使用ES5的Object.preventExtensions在將數(shù)據(jù)集對象交給Vue實例代理前將對象密封筐骇,這樣數(shù)據(jù)集對象就變成了不可拓展的了债鸡,Vue就不會再添加新的屬性了,也就無法設置setter和getter了铛纬。

做了這個處理后渲染幾十萬數(shù)據(jù)跟玩兒似的流暢厌均。但是阻止Vue設置getter和setter也造成了一些問題,比如原來表格組件中的一些依賴于表格數(shù)據(jù)的計算屬性饺鹃,現(xiàn)在無法在表格數(shù)據(jù)變化時重新計算莫秆,當然了,影響不大悔详,就一個表格行數(shù)的計算镊屎,所以改成了手動設置這個值。

到這里要講的差不多了茄螃,這只是項目中的一點優(yōu)化內(nèi)容缝驳,我封裝的vue-bigdata-table(沒辦法,好名字都被注冊了)表格組件不僅僅這點功能归苍,目前還包括拖動修改列寬用狱、固定列不橫向滾動,固定表頭拼弃、內(nèi)置排序夏伊、編輯單元格、粘貼吻氧、篩選溺忧、自定義表頭和單元格等功能。現(xiàn)在也已經(jīng)開源了盯孙,但是還有很多功能還在開發(fā)中鲁森。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市振惰,隨后出現(xiàn)的幾起案子歌溉,更是在濱河造成了極大的恐慌,老刑警劉巖骑晶,帶你破解...
    沈念sama閱讀 222,464評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件痛垛,死亡現(xiàn)場離奇詭異草慧,居然都是意外死亡,警方通過查閱死者的電腦和手機榜晦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評論 3 399
  • 文/潘曉璐 我一進店門冠蒋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來羽圃,“玉大人乾胶,你說我怎么就攤上這事⌒嗄” “怎么了识窿?”我有些...
    開封第一講書人閱讀 169,078評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長脑融。 經(jīng)常有香客問我喻频,道長,這世上最難降的妖魔是什么肘迎? 我笑而不...
    開封第一講書人閱讀 59,979評論 1 299
  • 正文 為了忘掉前任甥温,我火速辦了婚禮,結(jié)果婚禮上妓布,老公的妹妹穿的比我還像新娘姻蚓。我一直安慰自己,他們只是感情好匣沼,可當我...
    茶點故事閱讀 69,001評論 6 398
  • 文/花漫 我一把揭開白布狰挡。 她就那樣靜靜地躺著,像睡著了一般释涛。 火紅的嫁衣襯著肌膚如雪加叁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,584評論 1 312
  • 那天唇撬,我揣著相機與錄音它匕,去河邊找鬼。 笑死窖认,一個胖子當著我的面吹牛豫柬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播耀态,決...
    沈念sama閱讀 41,085評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼轮傍,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了首装?” 一聲冷哼從身側(cè)響起创夜,我...
    開封第一講書人閱讀 40,023評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎仙逻,沒想到半個月后驰吓,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體涧尿,經(jīng)...
    沈念sama閱讀 46,555評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,626評論 3 342
  • 正文 我和宋清朗相戀三年檬贰,在試婚紗的時候發(fā)現(xiàn)自己被綠了姑廉。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,769評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡翁涤,死狀恐怖桥言,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情葵礼,我是刑警寧澤号阿,帶...
    沈念sama閱讀 36,439評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站鸳粉,受9級特大地震影響扔涧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜届谈,卻給世界環(huán)境...
    茶點故事閱讀 42,115評論 3 335
  • 文/蒙蒙 一枯夜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧艰山,春花似錦湖雹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,601評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至织鲸,卻和暖如春舔腾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背搂擦。 一陣腳步聲響...
    開封第一講書人閱讀 33,702評論 1 274
  • 我被黑心中介騙來泰國打工稳诚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瀑踢。 一個月前我還...
    沈念sama閱讀 49,191評論 3 378
  • 正文 我出身青樓扳还,卻偏偏與公主長得像,于是被迫代替她去往敵國和親橱夭。 傳聞我的和親對象是個殘疾皇子氨距,可洞房花燭夜當晚...
    茶點故事閱讀 45,781評論 2 361

推薦閱讀更多精彩內(nèi)容