前端虚拟列表实现:别再渲染10000个DOM节点了

前端虚拟列表实现:别再渲染10000个DOM节点了

前端虚拟列表实现:别再渲染10000个DOM节点了

毒舌时刻

这代码写得跟网红滤镜似的——仅供参考。

各位前端同行,咱们今天聊聊前端虚拟列表。别告诉我你还在一次性渲染10000个列表项,那感觉就像把10000本书全部摆在桌面上——既占地方又难找。

为什么你需要虚拟列表

最近看到一个项目,一个下拉列表有5000个选项,全部渲染导致页面卡死,我差点当场去世。我就想问:你是在做列表还是在做性能杀手?

反面教材

// 反面教材:一次性渲染所有数据 function BigList({ items }) { return ( <ul style={{ height: '400px', overflow: 'auto' }}> {items.map(item => ( <li key={item.id} style={{ height: '50px' }}> {item.name} </li> ))} </ul> ); } // 使用 <BigList items={Array.from({ length: 10000 }, (_, i) => ({ id: i, name: `Item ${i}` }))} /> // 浏览器:我太难了 

毒舌点评:这代码,我看了都替浏览器着急。渲染10000个DOM节点,你是想让用户的电脑变成暖炉吗?

前端虚拟列表的正确姿势

1. 基础虚拟列表实现

// 正确姿势:基础虚拟列表 import { useState, useRef, useMemo, useCallback } from 'react'; function VirtualList({ items, itemHeight, containerHeight }) { const [scrollTop, setScrollTop] = useState(0); const containerRef = useRef(null); // 计算可见区域 const visibleCount = Math.ceil(containerHeight / itemHeight); const totalHeight = items.length * itemHeight; // 计算起始和结束索引 const startIndex = Math.floor(scrollTop / itemHeight); const endIndex = Math.min(startIndex + visibleCount + 1, items.length); // 获取可见项 const visibleItems = useMemo(() => { return items.slice(startIndex, endIndex); }, [items, startIndex, endIndex]); // 偏移量 const offsetY = startIndex * itemHeight; const handleScroll = useCallback((e) => { setScrollTop(e.target.scrollTop); }, []); return ( <div ref={containerRef} style={{ height: containerHeight, overflow: 'auto' }} onScroll={handleScroll} > <div style={{ height: totalHeight, position: 'relative' }}> <div style={{ transform: `translateY(${offsetY}px)` }}> {visibleItems.map((item, index) => ( <div key={item.id} style={{ height: itemHeight }} className="virtual-list-item" > {item.name} </div> ))} </div> </div> </div> ); } // 使用 function App() { const items = Array.from({ length: 10000 }, (_, i) => ({ id: i, name: `Item ${i}` })); return ( <VirtualList items={items} itemHeight={50} containerHeight={400} /> ); } 

2. 使用react-window

// 正确姿势:使用react-window import { FixedSizeList as List } from 'react-window'; function VirtualList({ items }) { const Row = ({ index, style }) => ( <div style={style} className="list-item"> {items[index].name} </div> ); return ( <List height={400} itemCount={items.length} itemSize={50} > {Row} </List> ); } // 使用 function App() { const items = Array.from({ length: 10000 }, (_, i) => ({ id: i, name: `Item ${i}` })); return <VirtualList items={items} />; } 

3. 使用vue-virtual-scroller

<!-- Vue3中使用vue-virtual-scroller --> <template> <RecycleScroller :items="items" :item-size="50" key-field="id" v-slot="{ item }" > <div> {{ item.name }} </div> </RecycleScroller> </template> <script setup> import { RecycleScroller } from 'vue-virtual-scroller'; import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'; const items = Array.from({ length: 10000 }, (_, i) => ({ id: i, name: `Item ${i}` })); </script> <style> .scroller { height: 400px; } .item { height: 50px; display: flex; align-items: center; padding: 0 20px; } </style> 

毒舌点评:早这么写,你的列表早流畅了。别告诉我你还在渲染10000个DOM节点,那你还是趁早去用分页吧。

实战技巧:虚拟列表指南

1. 虚拟列表原理

  1. 只渲染可见项:只渲染视口内的元素
  2. 计算偏移量:通过transform移动内容
  3. 动态高度支持:预估高度或动态计算

2. 最佳实践

  1. 固定高度:优先使用固定高度,性能好
  2. 缓冲区域:上下多渲染几个item,避免白屏
  3. 滚动优化:使用requestAnimationFrame

最后想说的

虚拟列表不是炫技,是性能优化。别再渲染10000个DOM节点了——虚拟化一下,你的应用会飞起来。

虚拟列表就像图书馆的检索系统,不需要把所有书都摆在桌上,只需要展示你想看的那几本。别做图书搬运工,做图书管理员。

Read more

唤醒80年代记忆:基于百度地图的一次老式天气预报的WebGIS构建之旅

唤醒80年代记忆:基于百度地图的一次老式天气预报的WebGIS构建之旅

目录 一、省会城市信息构建 1、省会城市空间查询 2、Java后台查询 二、Java省会城市天气查询 1、与百度开放平台集成天气 2、响应对象属性介绍 3、省会天气实况展示 三、WebGIS应用构建 1、背景音乐集成 2、城市标记及天气展示 3、城市轮播 4、成果展示 四、总结 前言         在数字技术飞速发展的今天,我们常常沉浸于各种高科技带来的便捷与震撼之中,却容易忽视那些曾经陪伴我们成长、承载着时代记忆的旧事物。80年代的天气预报,便是这样一份珍贵的文化遗产。它以简洁而质朴的方式,传递着天气信息,也传递着那个时代的气息。那种对自然的敬畏、对信息的渴望,以及一家人共同分享的温馨氛围,都深深烙印在我们的记忆中。然而,随着时间的推移,天气预报的形式已经发生了翻天覆地的变化。高清的画面、精准的数据、个性化的推送……这些现代技术带来的便利固然令人欣喜,但也在一定程度上让我们失去了那份对天气预报本身的纯粹情感。于是,

ollama v0.17.0 更新:OpenClaw 一键自动安装、Web 搜索支持、全新 Context 动态分配与 Tokenizer 性能大幅优化!

2026 年 2 月 24 日,Ollama 发布了全新的 v0.17.0 版本。这一次更新可谓意义重大,不仅引入了全新的 OpenClaw 自动化集成与安装能力,还针对核心 Tokenizer 性能、VRAM 动态上下文分配、系统配置迁移逻辑、Web 搜索插件 等功能进行了深层次优化。这是一次面向 AI 模型本地化与云端融合、性能与易用性双提升的版本升级。 本文将全面解析 Ollama v0.17.0 的更新亮点、底层代码变化及其背后的设计逻辑,帮助开发者和高级用户快速理解这一版本的重要意义。 一、版本概览 版本号: v0.17.0 发布日期: 2026 年 2 月 24 日

AI语音合成新趋势:大模型驱动的自然韵律生成入门必看

AI语音合成新趋势:大模型驱动的自然韵律生成入门必看 1. 为什么传统语音合成听起来“不像真人”? 你有没有听过这样的AI配音?语调平直、停顿生硬、重音错位,像机器人在念字典——哪怕音色再好,一听就出戏。这不是你的耳朵太挑剔,而是传统TTS(文本转语音)技术的固有局限。 传统方案大多依赖规则引擎或统计模型,把文字拆成音素、拼接波形,再靠人工调参加韵律。结果就是:能说清楚,但不会说话。它不懂哪句话该轻声试探,哪处该突然提高语速,更分不清“真的吗?”和“真的吗?”背后的情绪差异。 而IndexTTS-2-LLM的出现,正在改写这个逻辑。它不把语音当“声音拼图”,而是当作一种语言行为来理解——就像人说话前会先在脑子里组织意思、判断语气、设计节奏一样,它让大语言模型先“想清楚”,再“说出来”。 这带来的不是小修小补,而是质变:语音开始有了呼吸感、犹豫感、强调感,甚至一丝恰到好处的停顿留白。不是更像人,而是开始具备人说话时的思维节奏。

前端GraphQL客户端:优雅地获取数据

前端GraphQL客户端:优雅地获取数据 毒舌时刻 前端GraphQL?这不是后端的事吗? "REST API就够了,为什么要用GraphQL"——结果前端需要多次请求,数据冗余, "GraphQL太复杂了,我学不会"——结果错过了更灵活的数据获取方式, "我直接用fetch请求GraphQL,多简单"——结果缺少缓存、错误处理等功能。 醒醒吧,GraphQL不是后端的专利,前端也需要专业的客户端工具! 为什么你需要这个? * 减少网络请求:一次请求获取所有需要的数据 * 数据精确:只获取需要的数据,避免冗余 * 类型安全:自动生成TypeScript类型 * 缓存优化:智能缓存,减少重复请求 * 开发效率:简化数据获取逻辑 反面教材 // 反面教材:直接使用fetch请求GraphQL async function fetchGraphQL(query, variables) { const response = await