前端如何渲染几千几万条数据而不卡顿?
在 Vue 中渲染成千上万条数据时,直接渲染所有 DOM 节点会导致严重的性能问题(如内存占用高、渲染卡顿)。以下是几种常见的解决方法:
1. 使用虚拟滚动
只渲染可视区域内的元素,动态替换不可见区域的 DOM,极大减少实际渲染节点数。
npm install vue-virtual-scroller
{{ item.text }}import { RecycleScroller } from 'vue-virtual-scroller'; import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'; export default { components: { RecycleScroller }, data() { return { bigData: Array.from({ length: 10000 }, (_, i) => ({ id: i, text: `Item ${i}`, })), }; }, }; .scroller { height: 500px; } .item { height: 50px; padding: 10px; border-bottom: 1px solid #eee; }
2. 数据冻结
通过 Object.freeze 冻结数据,避免 Vue 3 的响应式系统追踪变化,减少内存开销。
Object.freeze() 静态方法可以使一个对象被冻结。冻结对象可以防止扩展,并使现有的属性不可写入和不可配置。
export default { data() { return { bigData: Object.freeze( Array.from({ length: 10000 }, (_, i) => ({ id: i, text: `Item ${i}`, })) ), }; }, };
3. 分片渲染
将数据分割成多个小块,利用浏览器的空闲时间(如 requestAnimationFrame)逐步渲染,避免一次性渲染所有 DOM 节点导致主线程阻塞。
requestAnimationFrame() 方法会告诉浏览器你希望执行一个动画。它要求浏览器在下一次重绘之前,调用用户提供的回调函数。
import { ref, onMounted } from 'vue'; export default { setup() { // 原始数据:1万条 const allData = ref( Array.from({ length: 10000 }, (_, i) => ({ id: i, text: `Item ${i}`, })) ); // 当前已渲染的数据 const visibleData = ref([]); // 每次渲染的数据量 const chunkSize = 100; // 当前渲染到的位置 const currentIndex = ref(0); // 是否正在加载 const isLoading = ref(false); // 分片渲染函数 const renderChunk = () => { isLoading.value = true; const end = currentIndex.value + chunkSize; // 截取下一块数据 visibleData.value = allData.value.slice(0, end); currentIndex.value = end; if (end { renderChunk(); }); return { visibleData, isLoading }; }, };{{ item.text }}加载中...
4. 优化 Vue 3 响应式数据
使用 shallowRef 或 shallowReactive:减少深层响应式开销。
import { shallowRef } from 'vue'; export default { setup() { const bigData = shallowRef( Array.from({ length: 10000 }, (_, i) => ({ id: i, text: `Item ${i}` })) ); return { bigData }; }, };
5. 分页加载
将数据分页加载,每次只渲染当前页的数据。
export default { data() { return { allData: Array.from({ length: 10000 }, (_, i) => ({ id: i, text: `Item ${i}` })), currentPage: 1, pageSize: 100, }; }, computed: { currentPageData() { const start = (this.currentPage - 1) * this.pageSize; const end = start + this.pageSize; return this.allData.slice(start, end); }, }, methods: { prevPage() { this.currentPage--; }, nextPage() { this.currentPage++; }, }, };{{ item.text }}上一页 下一页
(图片来源网络,侵删)
(图片来源网络,侵删)
(图片来源网络,侵删)
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。