Vue项目API大量数据获取与v-for渲染卡顿优化咨询
Vue列表渲染卡顿优化方案
一、优化数据获取逻辑,减少响应式更新次数
原代码在遍历API返回数据时,每次都通过数组展开重新赋值,导致几百条数据触发几百次Vue响应式更新,直接阻塞主线程。
优化方案:先在内存中构建完整数组,再一次性赋值给响应式变量,只触发两次更新:
async created() { this.loaded = false; try { const response = await fetch('https://api2.binance.com/api/v3/ticker/24hr'); const data = await response.json(); // 内存中预构建数组 const symbols = []; const prices = []; data.forEach(element => { symbols.push(element.symbol); prices.push(+element.lastPrice); }); // 一次性赋值,减少响应式触发次数 this.chartData.symbols = symbols; this.chartData.price = prices; this.loaded = true; } catch (e) { console.error(e); } },
二、给大数据列表添加虚拟滚动
Binance API返回几百条交易对,直接渲染全部DOM会导致节点数量暴增,搜索和滚动时卡顿。使用Vuetify的v-virtual-scroll只渲染可视区域节点。
优化方案:
- 替换原列表容器为虚拟滚动组件:
<!-- 替换原v-row --> <v-virtual-scroll :items="filteredPairs" height="600px" > <template v-slot="{ item: pair, index }"> <v-list-item :key="pair.symbol"> <v-list-item-content> <v-list-item-title>{{ pair.symbol }} - {{ pair.price }}</v-list-item-title> </v-list-item-content> <v-text-field outlined v-model="numberValue" hide-details single-line type="number" class="shrink mr-2" width="50px" height="50px" /> <AddBtn @click-event="searchHandle(index)" /> </v-list-item> </template> </v-virtual-scroll>
- 将搜索过滤逻辑改为computed缓存结果:
computed: { pairs() { return this.chartData.symbols.map((symbol, i) => ({ symbol, price: this.chartData.price[i] })); }, filteredPairs() { if (!this.search.trim()) return this.pairs; const keyword = this.search.toLowerCase(); return this.pairs.filter(pair => pair.symbol.toLowerCase().includes(keyword) ); } },
三、避免不必要的DOM操作和响应式更新
- 移除冗余的DOM引用:原代码用
ref="refWord"获取文本内容,易出错且增加DOM开销,直接通过过滤后的数组取数:
searchHandle(index) { const pair = this.filteredPairs[index]; this.chartData.labels = [...this.chartData.labels, pair.symbol]; this.chartData.datasets[0].data = [ ...this.chartData.datasets[0].data, pair.price ]; },
- 修正key的错误用法:主页面列表用
index++作为key会导致Vue无法正确复用DOM,改用唯一标识:
<v-list-item class="border" v-for="(label, index) in chartData.labels" :key="label" >
四、优化PieChart组件渲染逻辑
原组件未监听props变化,数据更新后图表无法正确刷新,且易触发重复渲染。
优化方案:添加watch监听props变化,动态更新图表数据:
export default { name: 'PieChart', components: {Pie}, props: ['data', 'bgColor', 'labels'], data() { return { chartData: { labels: [...this.labels], datasets: [ { data: [...this.data], backgroundColor: [...this.bgColor], }, ], }, emptyChartData: { labels: [], datasets: [ { data: [0.01], backgroundColor: ['#999797'], }, ], }, chartOptions: { responsive: true, elements: { arc: { borderWidth: 0, }, }, }, loaded: true, }; }, watch: { labels: { handler(newVal) { this.chartData.labels = [...newVal]; }, deep: true }, data: { handler(newVal) { this.chartData.datasets[0].data = [...newVal]; }, deep: true }, bgColor: { handler(newVal) { this.chartData.datasets[0].backgroundColor = [...newVal]; }, deep: true } } };
同时移除父组件中PieChart的:key="this.chartData.labels.length",避免组件频繁重建。
五、其他细节优化
- 替换页面刷新逻辑:原
location.reload()会导致全页重载,改为重新获取数据:
refreshPage() { this.chartData.symbols = []; this.chartData.price = []; this.created(); },
- 搜索输入防抖:用防抖函数减少搜索过滤的触发频率,避免输入时频繁计算:
import _ from 'lodash'; export default { created() { this.debouncedFilter = _.debounce((keyword) => { this.filterKeyword = keyword; }, 300); }, watch: { search(val) { this.debouncedFilter(val); } }, computed: { filteredPairs() { if (!this.filterKeyword?.trim()) return this.pairs; const keyword = this.filterKeyword.toLowerCase(); return this.pairs.filter(pair => pair.symbol.toLowerCase().includes(keyword) ); } } }
内容的提问来源于stack exchange,提问作者theplaceofburak
相关产品推荐
相关产品推荐

