技術專欄 | DMap——實戰Vue百萬條數據渲染表格組件開發
05-20
技術專欄 | DMap——實戰Vue百萬條數據渲染表格組件開發
推薦閱讀:
作者:TalkingData 李志剛
本文由TalkingData原創,轉載請獲取授權。李志剛:近幾個月在開發一個基於Vue的數據可視化分析輔助應用———DMap(諦聽),一套為數據分析師和數據科學家提供的基於位置大數據分析的工具,旨在提高數據分析效率,降低獲取多數據並行分析成本,簡化大屏和數據報告開發製作流程。其UI組件使用的是iView,地圖可視化庫使用的是inMap,服務端使用Node.js搭建。DMap的核心就是服務大數據分析,所以當面對幾萬幾十萬甚至百萬級別的數據時,性能優化是一個具有挑戰性的問題。今天我就拿項目中一個表格渲染的優化為例來展開介紹。
在前端開發中,用表格來展示數據是再平常不過的了,當數據量較多時,我們通常的做法是使用分頁,如果數據量不算太多只有兩三頁,我們大可以把全量數據獲取下來,在前端做簡單的分頁展示。當數據量再上一個等級時,我們就需要根據頁數向服務端請求這一頁需要的數據。但是DMap作為助力大數據可視化的分析工具,我們需要將全量的數據在前端做展示,而為了提升用戶體驗,我們在表格的展示上決定不做分頁,也不做懶載入,而是像Excel那樣可以無縫隙的滾動。在Web中,長列表渲染的性能問題已經有一些成熟的方案,表格和長列表相似,當渲染的行數達到一定量時,滾動就會變得卡頓,所以我們使用了虛擬渲染的方案,就是只渲染用戶所能看到的區域的一小部分數據,然後通過滾動來計算顯示的數據,和上下佔位元素的高度。通過這個圖可以對原理有個大概的了解,接下來說下計算上的細節。首先我們需要監聽表格外層容器(也就是顯示滾動條的元素)的scroll事件,在scroll事件綁定的方法中我們只做一件事,那就是獲取外層容器當前滾動了的高度scrollTop的值。我們的所有計算,包括三個表格位置的替換、表格數據的選取、上下佔位元素的高度的計算都與scrollTop相關。下面是scroll事件的綁定的方法:handleScroll (e) { const ele = e.srcElement || e.target; const { scrollTop, scrollLeft } = ele; this.scrollLeft = scrollLeft; this.scrollTop = scrollTop; }我們只需要在這裡把scrollTop和scrollLeft的值賦給vue實例對應的值,然後我們用watch監聽scrollTop的改變,如果更新了,就來計算當前處於可視區域的表格索引號currentIndex:
(註:左右滑動即可查看完整代碼,下同)this.currentIndex = parseInt((top % (this.moduleHeight * 3)) / this.moduleHeight);這的top就是更新後的this.scrollTop的值,moduleHeight是單個表格的高度,我們稱它為一個模塊。拿到currentIndex的值後,我們就可以計算三個表格的顯示位置,和每個表格中填充的數據。三個表格我們是通過render函數渲染的,我們根據currentIndex的值來返回不同順序的render函數: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];else if (this.currentIndex === 1) return [table2, table3, table1];else return [table3, table1, table2];}數組中表格順序不同,反應在頁面上的效果就是不同的先後順序。最後我們通過這個方法得到完整的render:renderTable (h) { return h(div, { style: this.tableWidthStyles }, this.getTables(h)); }然後使用封裝的無狀態的組件,來渲染我們得到的表格render。<render-dom :render="renderTable"></render-dom>renderDom組件的實現如下:
export default { name: RenderCell, functional: true, props: { render: Function, backValue: [Number, Object] }, render: (h, ctx) => { return ctx.props.render(h, ctx.props.backValue, ctx.parent); }};接下來我們講下三個表格中填充的數據的計算。我們按照三個模塊都在可視區域經過一次算是一輪,通過scrollTop來和currentIndex來計算每個模塊當前是在第幾輪展示,但因為我們是從第二個表格才開始做這個邏輯的處理(為了輪播效果更平滑),所以要先判斷當前滾動的高度是否大於一個模塊的高度,如果大於才做如下計算:switch (this.currentIndex) { case 0: t0 = parseInt(scrollTop / (this.moduleHeight * 3)); t1 = t2 = t0; break; case 1: t1 = parseInt((scrollTop - this.moduleHeight) / (this.moduleHeight * 3)); t0 = t1 + 1; t2 = t1; break; case 2: t2 = parseInt((scrollTop - this.moduleHeight * 2) / (this.moduleHeight * 3)); t0 = t1 = t2 + 1;}計算出每個模塊在第幾輪展示後,就可以來取對應的表格數據了: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);到這裡虛擬渲染的重要內容都介紹完了。表格開發完成後,在項目中實際使用時,當載入二十多萬條數據來測試時,整個頁面卡的讓人無法忍受,數據量越大頁面卡頓越嚴重。我們的表格是沒有問題的,問題出在Vue幫了我們「倒忙」。在Vue實例中添加的對象,Vue會先遍歷一遍對象的所有屬性,用——
Object.defineProperty()為每個對象創建對應的getter和setter。而在項目中,我們的insideTableData只是一個數據集對象中的一個屬性,這個對象還包括很多與這一個數據集相關的信息,我們在使用this.insideTableData.slice獲取數據的時候會觸發this.insideTableData對應的getter,從而執行一些其他邏輯,而我們的滾動又會頻繁的(僅當currentIndex變化的時候)需要重新填充表格數據,所以這會造成卡頓。解決這個問題的辦法就是阻止Vue給我們的數據集對象設置對應的setter和getter,我了解的有兩種方法,一是文檔中提到的:我們使用的時候就需要通過——this.$data._dataSet.insideTableData(這裡的_dataSet就是一個數據集對象)來獲取。另一種方法,就是使用ES5的Object.preventExtensions在將數據集對象交給Vue實例代理前將對象密封,這樣數據集對象就變成了不可拓展的了,Vue就不會再添加新的屬性了,也就無法設置setter和getter了。做了這個處理後渲染幾十萬數據跟玩兒似的流暢。但是阻止Vue設置getter和setter也造成了一些問題,比如原來表格組件中的一些依賴於表格數據的計算屬性,現在無法在表格數據變化時重新計算,當然了,影響不大,就一個表格行數的計算,所以改成了手動設置這個值。
到這裡要講的差不多了,這只是項目中的一點優化內容,我封裝的vue-bigdata-table(沒辦法,好名字都被註冊了)表格組件不僅僅這點功能,目前還包括拖動修改列寬、固定列不橫向滾動,固定表頭、內置排序、編輯單元格、粘貼、篩選、自定義表頭和單元格等功能。現在也已經開源了,但是還有很多功能還在開發中。推薦閱讀:
※Linux大數據開發之Shell編程基礎
※從頭學習大數據培訓課程 數據倉儲工具 hive(七)hive 自定義 UDTF
※大數據可能「說謊」 非結構化數據將呈現更豐富的世界
※《在線》的碎碎念
※移動計算比移動數據更划算 - 大數據技術源起
TAG:大數據 |