日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術(shù)文章
文章詳情頁

js前端對于大量數(shù)據(jù)的展示方式及處理方法

瀏覽:198日期:2024-04-11 10:18:00

最近暫時(shí)脫離了演示項(xiàng)目,開始了公司內(nèi)比較常見的以表單和列表為主的項(xiàng)目。干一個(gè),愛一個(gè)了。從開始的覺得自己都做了炫酷的演示項(xiàng)目了,這對我來說就是個(gè)小意思,慢慢也開始踩坑,有了些經(jīng)驗(yàn)總結(jié)可談。

現(xiàn)下不得不說是個(gè)數(shù)據(jù)的時(shí)代,有數(shù)據(jù)就必定有前端來展示。雜亂的數(shù)據(jù)通過數(shù)據(jù)分析(未碰到的點(diǎn),不講請搜),提煉出業(yè)務(wù)相關(guān)的數(shù)據(jù)維度,而前端所做的就是把這些一個(gè)個(gè)數(shù)據(jù)通過不同維度(key-value)的描述來展示到頁面上。

除去花哨的展示方式(圖表等),展示普通的大量列表數(shù)據(jù)有兩種常用方式,分頁和觸底加載(滾動(dòng)加載)。

分頁是一種比較經(jīng)典的展示方式,碰到的問題比較少,最多是因?yàn)橐豁撜故镜臄?shù)據(jù)量大些的時(shí)候可以用圖片懶加載,來加速一些(不過基本一頁也不太會超過200個(gè),不然就失去了分頁的意義了)。

js前端對于大量數(shù)據(jù)的展示方式及處理方法

而最近在實(shí)現(xiàn)滾動(dòng)加載時(shí),出現(xiàn)了卡頓的情況。

js前端對于大量數(shù)據(jù)的展示方式及處理方法

問題背景:

數(shù)據(jù)量:1500左右;數(shù)據(jù)描述形式:圖片 + 部分文字描述;卡頓出現(xiàn)在兩個(gè)地方:

滾動(dòng)卡頓,往往是動(dòng)一下滾輪,就要卡個(gè)2-3s單個(gè)數(shù)據(jù)卡片事件響應(yīng)卡頓:鼠標(biāo)浮動(dòng),本應(yīng)0.5s向下延展,但是延展之前也會卡個(gè)1-2s;鼠標(biāo)點(diǎn)擊,本應(yīng)彈出圖片大圖,但是彈出前也要卡個(gè)1-2s

js前端對于大量數(shù)據(jù)的展示方式及處理方法

分析過程:

卡頓首先想到是渲染幀被延長了,用控制臺的Performance查看,可以看出是重排重繪費(fèi)時(shí)間:

js前端對于大量數(shù)據(jù)的展示方式及處理方法

如圖,Recalculate Style占比遠(yuǎn)遠(yuǎn)大于其他,一瞬間要渲染太多的卡片節(jié)點(diǎn),重排重繪的量太大,所以造成了主要的卡頓。因此,需要減少瞬間的渲染量。

渲染的數(shù)據(jù)項(xiàng)與圖片渲染有關(guān),于是會想到圖片資源的加載和渲染,看控制臺的Network的Img請求中,有大量的pending項(xiàng)(pending項(xiàng)參考下圖所示)。

js前端對于大量數(shù)據(jù)的展示方式及處理方法

圖片在不停地加載然后渲染,影響了頁面的正常運(yùn)行,因此可以作懶加載優(yōu)化。

解決過程:

首先針對最主要的減少瞬間渲染量,逐步由簡入繁嘗試:

1. 自動(dòng)觸發(fā)的延時(shí)渲染由定時(shí)器來操作,setTimeout和setInterval都可以,注意及時(shí)跳出循環(huán)即可。我使用了setTimeout來作為第一次嘗試(下面代碼為后續(xù)補(bǔ)的手寫,大概意思如此)

使用定時(shí)器來分頁獲取數(shù)據(jù),然后push進(jìn)展示的列表數(shù)據(jù)中:

data() { return { count: -1, params: { ... // 請求參數(shù) pageNo: 0, pageSize: 20 }, timer:null, list: [] }},beforeDestroy() { if (this.timer) { clearTimeout(this.timer) this.timer = null }},methods: { getListData() { this.count = -1 this.params = { ... // 請求參數(shù) pageNo: 0, pageSize: 20 } this.timer = setTimeout(this.getListDataInterval, 1000) }, getListDataInterval() { params.pageNo++ if (params.pageNo === 1) { this.list.length = 0 } api(params) // 請求接口 .then(res => { if (res.data) { this.count = res.data.count this.list.push(...res.data.list) } }) .finally(() => { if (count >= 0 && this.list.length < count) { this.timer = setTimeout(this.getListDataInterval, 1000) } }) } ...}

結(jié)果:首屏渲染速度變快了,不過滾動(dòng)和事件響應(yīng)還是略卡頓。原因分析:滾動(dòng)的時(shí)候還是有部分?jǐn)?shù)據(jù)在渲染和加載,其次圖片資源的加載渲染量未變(暫未作圖片懶加載)。

2. 改為滾動(dòng)觸發(fā)加載(滾動(dòng)觸發(fā)下的“分頁”形容的是數(shù)據(jù)分批次)

滾動(dòng)觸發(fā),好處在于只會在觸底的情況下影響用戶一段時(shí)間,不會在開始時(shí)一直影響用戶,而且觸底也是由用戶操作概率發(fā)生的,相對比下,體驗(yàn)性增加。此處有兩種做法:

滾動(dòng)觸發(fā)“分頁”請求數(shù)據(jù),缺點(diǎn):除了第一次,之后每次滾動(dòng)觸發(fā)展示數(shù)據(jù)會比下一種耗費(fèi)多一個(gè)請求的時(shí)間一次性獲取所有數(shù)據(jù)存在內(nèi)存中,滾動(dòng)觸發(fā)“分頁”展示數(shù)據(jù)。缺點(diǎn):第一次一次性獲取所有數(shù)據(jù)的時(shí)間,比上一種耗費(fèi)多一點(diǎn)時(shí)間上述兩種做法,可視數(shù)據(jù)的具體數(shù)量決定(據(jù)同事所嘗試,兩三萬個(gè)數(shù)據(jù)的獲取時(shí)間在1s以上,不過這個(gè)也看數(shù)據(jù)結(jié)構(gòu)的復(fù)雜程度和后端查數(shù)據(jù)的方式),決定前可以調(diào)后端接口試一下時(shí)間。

例:結(jié)合我本次項(xiàng)目的實(shí)際情況,不需要一次性獲取所有的數(shù)據(jù),可以一次性獲取一個(gè)時(shí)間點(diǎn)的數(shù)據(jù),而每個(gè)時(shí)間點(diǎn)的數(shù)據(jù)不會超過3600個(gè),這就屬于一個(gè)比較小的量,嘗試下來一次性獲取的時(shí)間基本不超過500ms,于是我選擇第二種

先一次性獲取所有數(shù)據(jù),由前端控制滾動(dòng)到距離底部的一定距離,push一定量的數(shù)據(jù)到展示列表數(shù)據(jù)中:

data() { return { timer: null, list: [], // 存儲數(shù)據(jù)的列表 showList: [], // html中展示的列表 isLoading: false, // 控制滾動(dòng)加載 currentPage: 1, // 前端分批次擺放數(shù)據(jù) currentPageSize: 50, // 前端分批次擺放數(shù)據(jù) lastListIndex: 0, // 記錄當(dāng)前獲取到的最新數(shù)據(jù)位置 lastTimeIndex: 0, // 記錄當(dāng)前獲取到的最新數(shù)據(jù)位置 }},created() { // 優(yōu)化點(diǎn):可做可不做,其中的數(shù)值都是按照卡片的寬高直接寫入的,因?yàn)椴皇峭ㄓ媒M件,所以從簡。 this.currentPageSize = Math.round( (((window.innerHeight / 190) * (window.innerWidth - 278 - 254)) / 220) * 3 ) // (((window.innerHeight / 卡片高度和豎向間距) * (window.innerWidth - 列表內(nèi)容距視口左右的總距離 - 卡片寬度和橫向間距)) / 卡片寬度) * 3// *3代表我希望每次加載至少能多出三個(gè)視口高度的數(shù)據(jù);列表內(nèi)容距視口左右的總距離:是因?yàn)槲沂莾蛇吂潭▽挾龋虚g適應(yīng)展示內(nèi)容的結(jié)構(gòu)},beforeDestroy() { if (this.timer) { clearTimeout(this.timer) this.timer = null }},methods: { /** * @description: 獲取時(shí)間點(diǎn)的數(shù)據(jù) */ getTimelineData(listIndex, timeIndex) { if ( // this.list的第一、二層是時(shí)間軸this.list[listIdex].timeLines[timeIndex],在獲取時(shí)間點(diǎn)數(shù)據(jù)之前獲取了 this.list && this.list[listIndex] && this.list[listIndex].timeLines && this.list[listIndex].timeLines[timeIndex] && this.showList && this.showList[listIndex] && this.showList[listIndex].timeLines && this.showList[listIndex].timeLines[timeIndex] ) { this.isLoading = true // 把當(dāng)前時(shí)間點(diǎn)變成展示狀態(tài) if (!this.showList[listIndex].active) { this.handleTimeClick(listIndex, this.showList[listIndex]) } if (!this.showList[listIndex].timeLines[timeIndex].active) this.handleTimeClick( listIndex, this.showList[listIndex].timeLines[timeIndex] ) if (!this.list[listIndex].timeLines[timeIndex].snapDetailList) { this.currentPage = 1 } if ( !this.list[listIndex].timeLines[timeIndex].snapDetailList // 第一次加載時(shí)間點(diǎn)數(shù)據(jù),后面的或條件可省略 ) {return suspectSnapRecords({ ... }) .then(res => { if (res.data && res.data.list && res.data.list.length) { let show = [] res.data.list.forEach((item, index) => {show[index] = {}if (index < 50) { show[index].show = true} else { show[index].show = true} }) this.$set(this.list[listIndex].timeLines[timeIndex],’snapDetailList’,res.data.list ) this.$set(this.showList[listIndex].timeLines[timeIndex],’snapDetailList’,res.data.list.slice(0, this.currentPageSize) ) this.$set(this.showList[listIndex].timeLines[timeIndex],’showList’,show ) this.currentPage++ this.lastListIndex = listIndex this.lastTimeIndex = timeIndex } }) .finally(() => { this.$nextTick(() => { this.isLoading = false }) }) } else { // 此處是時(shí)間點(diǎn)被手動(dòng)關(guān)閉,手動(dòng)關(guān)閉會把showList中的數(shù)據(jù)清空,但是已經(jīng)加載過數(shù)據(jù)的情況 if ( this.showList[listIndex].timeLines[timeIndex].snapDetailList .length === 0 ) { this.currentPage = 1 this.lastListIndex = listIndex this.lastTimeIndex = timeIndex } this.showList[listIndex].timeLines[timeIndex].snapDetailList.push( ...this.list[listIndex].timeLines[timeIndex].snapDetailList.slice( (this.currentPage - 1) * this.currentPageSize, this.currentPage * this.currentPageSize ) ) this.currentPage++ this.$nextTick(() => { this.isLoading = false }) return } } else { return } }, /** * @description: 頁面滾動(dòng)監(jiān)聽,用的是公司內(nèi)部的框架,就不展示html了,不同框架原理都是一樣的,只是需要寫的代碼多與少的區(qū)別,如ElementUI的InfiniteScroll,可以直接設(shè)置觸發(fā)加載的距離閾值 */ handleScroll({ scrollTop, percentY }) { // 此處的scrollTop是組件返回的縱向滾動(dòng)的已滾動(dòng)距離,percentY則是已滾動(dòng)百分比 this.bus.$emit(’scroll’) // 觸發(fā)全局的滾動(dòng)監(jiān)聽,用于圖片的懶加載 this.scrolling = true if (this.timer) { // 防抖機(jī)制,直至滾動(dòng)停止才會運(yùn)行定時(shí)器內(nèi)部內(nèi)容 clearTimeout(this.timer) } this.timer = setTimeout(() => { requestAnimationFrame(async () => { // 因?yàn)閮?nèi)部有觸發(fā)重排重繪,所以把代碼放在requestAnimationFrame中執(zhí)行 let height = window.innerHeight if ( percentY > 0.7 && // 保證最開始的時(shí)候不要瘋狂加載,已滾動(dòng)70%再加載 Math.round(scrollTop / percentY) - scrollTop < height * 2 && // 保證數(shù)據(jù)量大后滾動(dòng)頁面長的時(shí)候不要瘋狂加載,在觸底小于兩倍視口高度的時(shí)候才加載 !this.isLoading // 保險(xiǎn),不同時(shí)運(yùn)行下面代碼,以防運(yùn)行時(shí)間大于定時(shí)時(shí)間 ) { this.isLoading = true let len = this.list[this.lastListIndex].timeLines[ this.lastTimeIndex ].snapDetailList.length // list為一次性獲取所有數(shù)據(jù)存在內(nèi)存中 if ((this.currentPage - 1) * this.currentPageSize < len) { // 前端分批次展示的情況 this.showList[this.lastListIndex].timeLines[this.lastTimeIndex ].snapDetailList.push(...this.list[this.lastListIndex].timeLines[ this.lastTimeIndex].snapDetailList.slice( (this.currentPage - 1) * this.currentPageSize, this.currentPage * this.currentPageSize) ) this.currentPage++ } else if ( this.list[this.lastListIndex].timeLines.length > this.lastTimeIndex + 1 ) { // 前端分批次展示完上一波數(shù)據(jù),該月份時(shí)間軸上下一個(gè)時(shí)間點(diǎn)存在的情況 await this.getTimelineData(this.lastListIndex,this.lastTimeIndex + 1 ) } else if (this.list.length > this.lastTimeIndex + 1) { // 前端分批次展示完上一波數(shù)據(jù),該月份時(shí)間軸上下一個(gè)時(shí)間點(diǎn)不存在,下一個(gè)月份存在的情況 await this.getTimelineData(this.lastListIndex + 1, 0) } } this.$nextTick(() => { this.isLoading = false this.scrolling = false }) }) }, 500) },

結(jié)果:首屏渲染和事件響應(yīng)都變快了,只是滑動(dòng)到底部的時(shí)候有些許卡頓。原因分析:滑動(dòng)到底部的卡頓,也是因?yàn)橐凰查g渲染一堆數(shù)據(jù),雖然比一次性展示所有的速度快很多,但是還是存在相比一次性展示不那么嚴(yán)重的重排和重繪,以及圖片不停加載渲染的情況。

3. 滾動(dòng)觸發(fā)+圖片懶加載

圖片懶加載可以解決每次渲染數(shù)據(jù)的時(shí)候因?yàn)閳D片按加載順序不停渲染產(chǎn)生的卡頓。滾動(dòng)觸發(fā)使用點(diǎn)2的代碼。提取通用的圖片組件,通過滾動(dòng)事件的全局觸發(fā),來控制每個(gè)數(shù)據(jù)項(xiàng)圖片的加載:如上,點(diǎn)2中已經(jīng)在handleScroll中設(shè)置了 this.bus.$emit(’scroll’) // 觸發(fā)全局的滾動(dòng)監(jiān)聽,用于圖片的懶加載

// main.jsVue.prototype.bus = new Vue()...

以下的在template中寫js不要學(xué)噢

// components/DefaultImage.vue<template> <div ref='image'> <img src='http://m.b3g6.com/bcjs/@/assets/images/image_empty.png' v-if='imageLoading' /> <img v-if='showSrc' v-show='!imageLoading && !imageError' :src='http://m.b3g6.com/bcjs/showSrc' @load='imageLoading = false' @error=' imageLoading = false imageError = true ' /> <img src='http://m.b3g6.com/bcjs/@/assets/images/image_error.png' v-if='imageError' /> </div></template><script>export default { name: ’DefaultImage’, props: { src: String, // 圖片源 lazy: Boolean // 懶加載 }, data() { return { imageLoading: true, imageError: false, showSrc: ’’, // 渲染的src timer: null } }, mounted() { if (this.lazy) { this.$nextTick(() => { this.isShowImage() }) this.bus.$on(’scroll’, this.handleScroll) } else { this.showSrc = this.src } }, beforeDestroy() { if (this.lazy) { this.bus.$off(’scroll’, this.handleScroll) } if (this.timer) { clearTimeout(this.timer) this.timer = null } }, methods: { handleScroll() { if (this.timer) { clearTimeout(this.timer) } this.timer = setTimeout(this.isShowImage, 300) }, isShowImage() { let image = this.$refs.image if (image) { let rect = image.getBoundingClientRect() const yInView = rect.top < window.innerHeight && rect.bottom > 0 const xInView = rect.left < window.innerWidth && rect.right > 0 if (yInView && xInView) { this.showSrc = this.src this.bus.$off(’scroll’, this.handleScroll) } } } }}</script>

結(jié)果:在點(diǎn)2首屏展示快的基礎(chǔ)上,事件交互更快了,觸發(fā)展示數(shù)據(jù)也快了。原因分析:防抖的圖片懶加載之后,只在用戶滾動(dòng)停止時(shí),加載視口內(nèi)的圖片,就沒有后續(xù)不斷的加載渲染圖片,也就不會因?yàn)椴煌d秩緢D片而影響事件交互和基礎(chǔ)的無圖卡片渲染。

以上一頓操作之后已經(jīng)符合本項(xiàng)目的需求了。不過我研究了一下進(jìn)階操作 🤔還可以只渲染視口元素,非視口用padding代替,以及把計(jì)算過程放在Web Worker多線程執(zhí)行,進(jìn)一步提升速度。待我研究一下操作補(bǔ)上

以上就是js前端對于大量數(shù)據(jù)的展示方式及處理方法的詳細(xì)內(nèi)容,更多關(guān)于js 大量數(shù)據(jù)展示及處理的資料請關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: JavaScript
相關(guān)文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
欧美激情视频一区二区三区免费 | 精品欧美久久| 久久中文字幕一区二区三区| 国产精品免费大片| 精品一区二区三区亚洲| 精品99久久| 日韩中文在线电影| 亚洲成人免费| 亚洲精品观看| 国产精品极品在线观看| 久久香蕉网站| 神马午夜久久| 美女日韩在线中文字幕| 日韩精品91亚洲二区在线观看| 欧美一区影院| 亚洲精品**中文毛片| 亚洲精品一二三区区别| 欧美在线资源| 日本99精品| 中文av在线全新| 99国产精品久久久久久久成人热| 一区二区日韩免费看| 国产亚洲精品精品国产亚洲综合 | 亚洲精一区二区三区| 日本午夜精品一区二区三区电影 | 欧美亚洲综合视频| 中文在线а√天堂| 久久国产高清| 色网在线免费观看| 日韩精品亚洲aⅴ在线影院| 97se综合| 国产精品久久久久久久免费软件| 欧美中文一区二区| 国产精品啊v在线| 一区二区自拍| 国产粉嫩在线观看| 日本视频一区二区| 亚洲激情婷婷| 国产麻豆久久| 精品视频自拍| 男女男精品视频网| 天堂资源在线亚洲| 久久三级毛片| 久久国产人妖系列| 亚洲资源在线| 伊人久久婷婷| 久久中文亚洲字幕| 精品视频网站| 国产精品日本一区二区不卡视频 | 国产成人免费| 国产美女久久| 91亚洲精品在看在线观看高清| 亚洲性色视频| 99久久久久国产精品| 黄色网一区二区| 久久精品一区二区国产| 国产欧美日韩一区二区三区四区 | 日韩av二区在线播放| 亚洲视频二区| 国产亚洲精品久久久久婷婷瑜伽| 日韩欧美不卡| 欧美不卡高清一区二区三区| 国产成人久久精品麻豆二区| 国产劲爆久久| 欧美成人精品午夜一区二区| 欧美黄页在线免费观看| 国产乱码精品一区二区三区亚洲人 | 国产乱人伦精品一区| 久久先锋影音| 日本不卡一区二区三区| 国产精品久久久久毛片大屁完整版 | 在线成人直播| 美女国产一区| 国产图片一区| 亚洲美女久久精品| 不卡一区综合视频| 快she精品国产999| 奇米狠狠一区二区三区| 国产一区二区三区网| 中文字幕系列一区| 久久亚洲欧美| 亚洲人成精品久久久| 国产精品66| 蜜桃国内精品久久久久软件9| 免费视频一区二区| 国产精品二区影院| 少妇精品导航| 日韩高清不卡一区二区| 福利一区二区| 亚洲在线成人| 久久精品毛片| 欧美日一区二区| 欧美日韩一区二区三区不卡视频 | 麻豆成人在线观看| 久久精品卡一| 国产午夜精品一区在线观看| 欧产日产国产精品视频| 热久久国产精品| 日韩欧美另类一区二区| 一区二区国产在线| 亚洲精品**中文毛片| 日本一区福利在线| 99久久99久久精品国产片果冰 | 午夜在线视频观看日韩17c| 免费看日韩精品| 日韩电影免费在线观看| 亚洲视频二区| 欧美日韩激情在线一区二区三区| 亚洲五月综合| 图片区亚洲欧美小说区| 久久精品国产久精国产爱| 噜噜噜躁狠狠躁狠狠精品视频| 国产精品精品| 国产精品115| 亚洲3区在线| 免费中文字幕日韩欧美| 久久精品导航| 欧美国产极品| 欧美日韩中文| 在线亚洲精品| 91精品一区国产高清在线gif | av成人国产| 久久精品卡一| 国产精品成人一区二区不卡| 人人爱人人干婷婷丁香亚洲| 日韩精品一卡二卡三卡四卡无卡| 91精品国产福利在线观看麻豆| 国模精品一区| 久久精品国产福利| 国产在线一区不卡| 国产亚洲观看| 国产精品日韩精品在线播放| 国产日韩一区| 国产日韩欧美中文在线| 青草国产精品久久久久久| 日韩国产成人精品| 亚洲日产国产精品| 亚洲女同中文字幕| 男女男精品网站| 欧美一区二区三区久久| 国产精品久久久亚洲一区| 久久99精品久久久野外观看| 国产一区二区三区久久久久久久久| 国产成人77亚洲精品www| 中文无码日韩欧| 亚洲另类视频| 老司机精品久久| 国产成人精品一区二区免费看京| 日韩综合小视频| 欧美日韩va| 精品久久视频| 国产精品呻吟| 深夜日韩欧美| 丝袜诱惑一区二区| 亚洲欧美久久| 久久不见久久见免费视频7| 精品视频在线你懂得| 日韩免费小视频| 亚洲精品成人一区| 久久中文字幕一区二区三区| 日本韩国欧美超级黄在线观看| 激情婷婷综合| 久久字幕精品一区| 亚洲一区不卡| 日韩成人精品一区二区| 欧美永久精品| 欧美在线不卡| 免费黄色成人| 国产精品免费99久久久| 99精品电影| 四虎精品一区二区免费| 日韩av自拍| 日韩黄色免费网站| 91精品国产成人观看| 精品视频高潮| 视频一区二区中文字幕| 成人精品动漫一区二区三区| 蜜桃久久久久久| 日韩精品免费一区二区在线观看 | 久久久久久夜| 欧美日韩精品一区二区三区视频| 欧美精品羞羞答答| 欧美国产另类| 欧美伊人久久| 久久国产人妖系列| 欧美日一区二区在线观看| 热久久久久久久| 综合国产在线| 欧美日韩视频一区二区三区| 亚洲黄色网址| 精品欧美一区二区三区在线观看| 国产精品99一区二区三| 老司机精品视频在线播放| 国产日韩视频在线| 国产欧美一区二区三区国产幕精品| 日韩在线网址| 日韩极品在线观看| 国产乱人伦丫前精品视频 | 久久精品亚洲人成影院| 国产不卡人人|