{"id":28220425,"url":"https://github.com/septemus/react-virtuallist","last_synced_at":"2026-04-27T11:31:21.851Z","repository":{"id":248523391,"uuid":"828918421","full_name":"Septemus/react-virtualList","owner":"Septemus","description":"基于react的虚拟列表demo","archived":false,"fork":false,"pushed_at":"2024-07-15T12:17:36.000Z","size":948,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-11T15:39:02.029Z","etag":null,"topics":["react","virtual-list"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Septemus.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-07-15T11:45:00.000Z","updated_at":"2024-07-15T12:17:39.000Z","dependencies_parsed_at":"2024-07-15T14:40:19.308Z","dependency_job_id":null,"html_url":"https://github.com/Septemus/react-virtualList","commit_stats":null,"previous_names":["septemus/react-virtuallist"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Septemus/react-virtualList","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Septemus%2Freact-virtualList","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Septemus%2Freact-virtualList/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Septemus%2Freact-virtualList/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Septemus%2Freact-virtualList/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Septemus","download_url":"https://codeload.github.com/Septemus/react-virtualList/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Septemus%2Freact-virtualList/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32335295,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-26T23:26:28.701Z","status":"online","status_checked_at":"2026-04-27T02:00:06.769Z","response_time":128,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["react","virtual-list"],"created_at":"2025-05-18T04:15:24.959Z","updated_at":"2026-04-27T11:31:21.835Z","avatar_url":"https://github.com/Septemus.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 基于react的虚拟列表实现demo\n\n虚拟化列表是仅渲染其项目子集并用占位符替换不可见项目的列表.虚拟化技术可以通过减少需要创建和更新的 `DOM` 节点数量来显著提高渲染长列表的性能.\n\n## 构建步骤\n\n要从头开始构建虚拟化列表,我们将遵循以下步骤：\n\n### 为列表创建一个组件\n\n构建虚拟化列表的第一步是创建一个代表列表的组件.我们可以创建一个以项目数组为 `state` 的功能组件\n```typescript\nclass List extends React.Component\u003cpropType,stateType,any\u003e {\n    constructor(props:propType) {\n        super(props)\n        this.state={\n            visibleItems:[],\n        }\n    }\n    render() {\n        return (\n            \u003cdiv\u003e\n                \u003cul\u003e\n                    {\n                        this.state.visibleItems.map(\n                            (item) =\u003e (\n                                \u003cli key={item.id}\u003e{item.content}\u003c/li\u003e\n                            )\n                        )\n                    }\n                \u003c/ul\u003e\n            \u003c/div\u003e\n        );    \n    }\n}\n```\n在此示例中,我们使用 `map` 方法迭代项目数组并将每个项目渲染为 `li` 元素.我们还为每个项目分配一个唯一键,以帮助 `React` 识别它们\n\n### 规定容器高度和列表项高度\n\n构建虚拟化列表的第二步是仅渲染列表项的子集.为此,我们需要知道每个项目的高度以及容纳列表的容器的高度.\n\n```typescript\nimport React from \"react\"\nexport type item={\n    id:string,\n    content:string\n}\ntype propType={\n    items:item[],\n    width?:number,\n    height?:number,\n    lineHeight?:number\n}\ntype stateType={\n    visibleItems:item[],\n}\nclass List extends React.Component\u003cpropType,stateType,any\u003e {\n    constructor(props:propType) {\n        super(props)\n        this.state={\n            visibleItems:[],\n        }\n    }\n    render() {\n        const {width,height=500,lineHeight=40,items}=this.props\n        console.log('render')\n        return (\n            \u003cdiv \n                style={{\n                    width:width?`${width}px`:'100%',\n                    height:`${height}px`,\n                    overflowY:\"scroll\",\n                    position:\"relative\"\n                }} \n            \u003e\n                \u003cul\n                    style={{\n                        height:`${items.length * lineHeight-this.state.startIndex*lineHeight}px`,\n                    }}\n                \u003e\n                    {this.state.visibleItems.map((item) =\u003e (\n                        \u003cli key={item.id} style={{\n                            lineHeight:`${lineHeight}px`,\n                            border:\"solid 1px red\"\n                        }}\u003e{item.content}\u003c/li\u003e\n                    ))}\n                \u003c/ul\u003e\n                \n            \u003c/div\u003e\n        );    \n    }\n    \n}\n```\n在此更新的示例中,我们向组件添加了 `lineHeight` 和 `Height` 属性\n\n### 根据当前滑动距离进行切片\n\n```typescript\nexport type item={\n    id:string,\n    content:string\n}\ntype propType={\n    items:item[],\n    width?:number,\n    height?:number,\n    lineHeight?:number\n}\ntype stateType={\n    visibleItems:item[],\n    startIndex:number,\n    // invisibleItemsHeight:number\n}\nconst container=React.createRef\u003cHTMLDivElement\u003e()\nclass List extends React.Component\u003cpropType,stateType,any\u003e {\n    constructor(props:propType) {\n        super(props)\n        this.state={\n            visibleItems:[],\n            startIndex:0,\n        }\n    }\n    render() {\n        const {width,height=500,lineHeight=40,items}=this.props\n        console.log('render')\n        return (\n            \u003cdiv \n                style={{\n                    width:width?`${width}px`:'100%',\n                    height:`${height}px`,\n                    overflowY:\"scroll\",\n                    position:\"relative\"\n                }} \n                ref={container}\n                onScroll={this.updList}\n            \u003e\n                \u003cul\n                    style={{\n                        position:\"absolute\",\n                        top:`${this.state.startIndex*lineHeight}px`,\n                        width:\"80%\",\n                        height:`${items.length * lineHeight-this.state.startIndex*lineHeight}px`,\n                    }}\n                \u003e\n                    {this.state.visibleItems.map((item) =\u003e (\n                        \u003cli key={item.id} style={{\n                            lineHeight:`${lineHeight}px`,\n                            border:\"solid 1px red\"\n                        }}\u003e{item.content}\u003c/li\u003e\n                    ))}\n                \u003c/ul\u003e\n                \n            \u003c/div\u003e\n        );    \n    }\n    componentDidMount(): void {\n        this.updList()\n    }\n    updList=():void=\u003e{\n        const {items,lineHeight=40,height=500}=this.props\n        const startIndex = Math.floor(container.current?.scrollTop as number/lineHeight)\n        const endIndex=startIndex+Math.floor(height/lineHeight)\n        // const invisibleItemsHeight = (startIndex + this.state.visibleItems.length - endIndex) * lineHeight;\n        this.setState({\n            visibleItems:items.slice(startIndex, endIndex),\n            startIndex,\n            // invisibleItemsHeight\n        })\n    }\n    \n}\n```\n\n我们根据 `lineHeight` 和 `height` 计算 `startIndex` 和 `endIndex`.`startIndex` 表示第一个可见项目的索引,`endIndex` 表示最后一个可见项目的索引.\n\n然后我们使用切片方法从 `items` 数组中仅提取可见项\n\n我们将可见项呈现为高度为 `items.length * lineHeight-startIndex*lineHeight` 的 `ul` 元素.我们还使用 `top` 属性定位容器,该属性等于 `startIndex * itemHeight`\n\n最后,我们使用 `onScroll` 钩子调用 `updList` 函数以在用户滚动时更新 `visibleItems`和`startIndex` 状态\n\n## 运行效果\n\n![demo](./static/demo.gif)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseptemus%2Freact-virtuallist","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fseptemus%2Freact-virtuallist","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseptemus%2Freact-virtuallist/lists"}