当前位置: 首页 > news >正文

网站做百度排名教程太原搜索排名提升

网站做百度排名教程,太原搜索排名提升,公司网站建设制作商,如何开发一个聊天软件[React]如何提高大数据量场景下的Table性能? 两个方向:虚拟列表,发布订阅 虚拟列表 虚拟列表实际上只对可视区域的数据项进行渲染 可视区域(visibleHeight): 根据屏幕可视区域动态计算或自定义固定高度数据渲染项&…

[React]如何提高大数据量场景下的Table性能?

两个方向:虚拟列表,发布订阅

虚拟列表

虚拟列表实际上只对可视区域的数据项进行渲染

  • 可视区域(visibleHeight): 根据屏幕可视区域动态计算或自定义固定高度
  • 数据渲染项(visibleCount):可视区域除以行高并向下取整
  • startIndex: 初始为0,听过滚动条偏移计算
  • endIndex: startIndex + visibleCount; 数据结束位置索引,可额外预加载几条数据

实现思路

监听逻辑实现

  useEffect(() => {const onScrollChange = (e: React.WheelEvent) => {const top = (e.target as HTMLElement).scrollTopconst index = Math.floor(top / rowHeight)setScrollTop(top)setStartIndex(index ? index + 1 : 0)}virtualizedRef.current.addEventListener('scroll', onScrollChange)return () => {if (virtualizedRef.current) {virtualizedRef.current.removeEventListener('scroll', onScrollChange)}}}, [])

HTML结构如下:

  • virtualized_placeholder: 容器内占位,高度为列表总高度,撑满父容器,用于可视区域形成滚动条
<div ref={virtualizedRef} style={{ height: visibleHeight }}><table style={{ transform: `translate3d(0px, ${scrollTop}px, 0)` }}><thead>{...}</thead><tbody>{...}</tbody></table><div className="virtualized_placeholder" style={{ height: placeHeight }} /></div>

主要逻辑

  • 设置容器占位高度,计算可视区域数据项
  • 监听容器滚动事件,计算偏移距离,startIndex,组件卸载移除滚动事件
  • startIndex作为deps依赖项,当发生改变更新展示数据
useEffect(() => {const placeH = ((dataSource.length) * rowHeight) + rowHeightsetPlaceHeight(placeH)setVisibleCount(Math.floor(visibleHeight / rowHeight) + 2)}, [dataSource, rowHeight])useEffect(() => {const onScrollChange = (e: React.WheelEvent) => {const top = (e.target as HTMLElement).scrollTopconst index = Math.floor(top / rowHeight)setScrollTop(top)setStartIndex(index ? index + 1 : 0)}virtualizedRef.current.addEventListener('scroll', onScrollChange)return () => {if (virtualizedRef.current) {virtualizedRef.current.removeEventListener('scroll', onScrollChange)}}}, [])useEffect(() => {const data = dataSource.slice(startIndex, startIndex + visibleCount)setShowData(data)}, [startIndex, visibleCount, dataSource])

完整代码

/*** dataSource 数据数组 object[]* columns 表格列 string[]* rowKey 表格行key的取值 number | string* rowHeight tr固定高度 number* visibleHeight 可视区域高度 number* hasOrder 是否含有序号 boolean* orderTitle 序号标题 string*/
import React, { FC, useEffect, useState, useRef, memo } from 'react'
import { Empty } from 'antd';
import './index.less'interface DataProps {[key:string]: any
}
interface VirtualProps {dataSource: DataProps[]columns: string[]rowKey?: number | stringhasOrder?: booleanorderTitle?: stringrowHeight?: numbervisibleHeight?: number
}const Index: FC<VirtualProps> = (props) => {const {dataSource = [],columns = [],rowKey,hasOrder = false,orderTitle = '序号',rowHeight = 40,visibleHeight = 800,} = propsconst [startIndex, setStartIndex] = useState(0)const [placeHeight, setPlaceHeight] = useState(0)const [scrollTop, setScrollTop] = useState(0)const [visibleCount, setVisibleCount] = useState(0)const [showData, setShowData] = useState<DataProps[]>([])const virtualizedRef = useRef<any>(null)useEffect(() => {const placeH = ((dataSource.length) * rowHeight) + rowHeightsetPlaceHeight(placeH)setVisibleCount(Math.floor(visibleHeight / rowHeight) + 2)}, [dataSource, rowHeight])useEffect(() => {const onScrollChange = (e: React.WheelEvent) => {const top = (e.target as HTMLElement).scrollTopconst index = Math.floor(top / rowHeight)setScrollTop(top)setStartIndex(index ? index + 1 : 0)}virtualizedRef.current.addEventListener('scroll', onScrollChange)return () => {if (virtualizedRef.current) {virtualizedRef.current.removeEventListener('scroll', onScrollChange)}}}, [])useEffect(() => {const data = dataSource.slice(startIndex, startIndex + visibleCount)setShowData(data)}, [startIndex, visibleCount, dataSource])return (<div className="galois_virtualized_container" ref={virtualizedRef} style={{ height: visibleHeight }}><tablestyle={{ transform: `translate3d(0px, ${scrollTop}px, 0)` }}className="galois_virtualized_table"><thead><tr>{hasOrder && <th key="galois_index">{orderTitle}</th>}{columns.map(values => <th key={values}>{values}</th>)}</tr></thead><tbody>{showData.map((item, index) => (<tr key={rowKey ? item[rowKey] : index}>{hasOrder && <td>{startIndex + index + 1}</td>}{columns.map((values, ind) => <td key={ind}>{item[values]}</td>)}</tr>))}</tbody></table>{showData.length === 0 &&  <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}<div className="galois_virtualized_placeholder" style={{ height: placeHeight }} /></div>)
}export default memo(Index)

利用发布订阅模式优化批量编辑的场景

正常情况下来说,把整个表格的数据都挂载到一个state中是最简单的,但是这么做的话,每次单元格在编辑onChange的时候就会setState,从而更新整个table,在数据稍大的场景下,编辑的性能会非常低,用户每输入一个字都要rerender。

发布订阅可以帮我们去掉这一部分冗余的rerender,从而做到每个cell的onChange都是单独的。

预期下的单元格状态维护

每个cell都进行单独的状态管理,每个cell内部都是用

const [data, setData] = useState(defaultValue)return <Input value={data} onChange={(e)=>setData(e.target.value)}/>

来维护内容,这样的话即使onChange,也只是rerender这个单独的cell,不会影响到整个table。

发布订阅实现

export interface IRef {id: string[key: string]: any
}// 发布订阅模式
export class RefCollection {// 订阅者集合private refMap: Map<string, IRef>constructor() {this.refMap = new Map<string, IRef>()}// 添加订阅者public addRef(ref: IRef) {if (!this.refMap.has(ref.id)) {this.refMap.set(ref.id, ref)}}// 移除订阅者public removeRef(ref: IRef) {this.refMap.delete(ref.id)}// -----------------------下面是广播事件----------------------------------// 触发所有deps的submit方法public submit() {return Array.from(this.refMap.values()).map((oneRef => {return oneRef.submit?.()}))}// 触发所有deps的validate方法public validate() {return Array.from(this.refMap.values()).map((oneRef => {return oneRef.validate?.()}))}// ...其它
}

收集每个单元格的依赖

业务组件内:

// 注册一个收集器
const refCollection = useRef(new RefCollection())const columns = [{dataIndex: 'title',render(){return <Cell refCollection={refCollection}/>}}
]

单元格内部逻辑:

// 每一个Cell内const Cell = (props)=>{const { refCollection } = props// 每一个Cell内部自己实现接口,逻辑独立,只需关注自己即可const ref = useRef<any>({// 当前单元格的唯一标识id: uid()// 这里随便加什么属性,可以加一些type来区别不同的Cell// 比如说有些是Select的控件,有些是Input的控件// 在submit的时候就可以根据type来过滤收集type: "inputRender",// 一般来说,可能要给定一个行号,因为我们提交数据的时候都是按行提交的// 有了行ID之后我们可以在submit的时候聚合数据,转换成需要提交的格式tableRowId: row?.tableRowId,validate: useMemoizedFn(() => {// 在这里实现自己的validate方法 // refCollection执行validate的时候会遍历每一个订阅者的validate方法// return boolean}),submit: useMemoizedFn(() => {// 在这里实现自己的submit方法// refCollection执行validate的时候会遍历每一个订阅者的submit方法// return {}}),})// 在这里收集依赖useEffect(() => {if (!refCollection) returnrefCollection?.add(ref.current)return () => {refCollection?.remove(ref.current)}}, [])return <div></div>
}

提交阶段


const refCollection = useRef(new RefCollection())const onSubmit = ()=>{await refCollection.current.validateAll()const data = refCollection.current.submit()// 提交逻辑 data
}

为什么不用FormItem?

  • FormItem包含了其它很多逻辑,但是未必都需要用得上
  • 如果一个单元格就要多渲染一层FormItem,整体下来就会非常地损耗性能
  • FormItem如果不渲染出来,那么就无法做逻辑,而如果通过统一的状态管理,可以实现字段不渲染出来就能完成值的读取和修改,实现虚拟字段的效果(这时候可以搭配分页、虚拟列表提高性能),同时也能正常地兼顾一些联动操作(比如说表格数字汇总)
http://www.yidumall.com/news/64507.html

相关文章:

  • 北京seo公司华网白帽手机优化大师官方版
  • 网站如何做支付宝接口天津seo网络营销
  • 网站制作顺序百度百科官网首页
  • 用模板做的网站权重高吗dw友情链接怎么设置
  • 网站公司怎么做业务安卓优化大师
  • 济南网站建设公司电子商务网站域名注册平台
  • 南京网站建设要多少钱seoul是什么意思
  • 网页站点的用途手机seo排名软件
  • 怎么样让网站网址有图标非企户百度推广
  • 北京环评在那个网站上做竞价排名的优缺点
  • 哪些网站专做新闻站长网站优化公司
  • 物流网站如何设计南宁seo教程
  • wordpress 评论双击南昌seo全网营销
  • 企业网站建设财务规划水果店推广营销方案
  • 足球网站怎么做如何做网站营销
  • 重庆科技网站建设中央新闻今日要闻
  • 找公司建网站报价企业seo培训
  • 慧聪网怎样做网站友情链接免费自学电商教程
  • 南昌市住房和城乡建设局网站电商平台开发
  • 中国网站建设世界排名免费网站推广网址
  • 网站模板开发平台怎么做2023年5月最新疫情
  • 手机网站开发需要哪些人才百度地图人工电话
  • 南宁网站建设公司seo优化兰州百度推广的公司
  • 水利建筑工程网站优秀网站设计赏析
  • 建设代购彩票站网站最新百度快速排名技术
  • 彭阳门户网站建设推广方案怎么写模板
  • 如何做网站页面怎么注册域名网址
  • wordpress子页面不显示不出来内江seo
  • 酒类网站建设策划书百度app安装下载
  • 郑州网站推广有什么可以做推广的软件