欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解JS如何解決大數(shù)據(jù)下滾動頁面卡頓問題

 更新時間:2023年07月28日 11:29:28   作者:demo123567  
之前遇到不分頁直接獲取到全部數(shù)據(jù),前端滾動查看數(shù)據(jù),頁面就挺卡頓的,所以這篇文章來和大家聊聊如何解決這一問題,感興趣的小伙伴可以了解下

前言

之前遇到不分頁直接獲取到全部數(shù)據(jù),前端滾動查看數(shù)據(jù),頁面就聽卡頓的,當然這和電腦瀏覽器性能啥的還是有點關(guān)系。但根源還是一次性渲染數(shù)據(jù)過多導致的,因此就想到解決這個問題,最常見就是虛擬滾動,實現(xiàn)只渲染當前可見的部分,這樣瀏覽器一次性渲染的數(shù)據(jù)少了。 本文介紹虛擬列表和虛擬Table的實現(xiàn),基于React + ts技術(shù)棧。

虛擬列表

虛擬列表通過僅渲染當前可見區(qū)域的列表項來解決這個問題。它利用瀏覽器的滾動事件,根據(jù)用戶可見區(qū)域的大小和滾動位置動態(tài)地計算應(yīng)該渲染哪些列表項。這樣,即使數(shù)據(jù)量很大,也只有當前可見的列表項會被渲染,大大減少了DOM元素的數(shù)量,提高了頁面性能和響應(yīng)性。 結(jié)合下圖想象一下

實現(xiàn)虛擬列表的方法主要涉及以下步驟:

  • 計算可見區(qū)域:根據(jù)容器的尺寸(假如500px)和每一項的高度(50px),計算出可見的列表項數(shù)量。然后可視的數(shù)據(jù)就是10個。
  • 監(jiān)聽滾動事件:在容器上添加滾動事件監(jiān)聽,以便實時獲取滾動位置。為了容器可滾動,需要在容器內(nèi)添加空的帶有高度的元素,將父元素撐開,然后可滾動。獲取scrollTop的高度,就能計算出當前顯示第一項的下標(scrollTop / itemHeight),動態(tài)更新數(shù)據(jù)。

基于上面的思路,封裝一個滾動列表組件。

import _ from "lodash";
import React, { useEffect, useState } from "react";
import { listData } from "./data";
type ListType = {
  itemHeight?: number; // 每一項的高度
  visibleHeight?: number; // 可見高度
  total?: number; // 數(shù)據(jù)總數(shù)
  dataSource?: any[]; // 全部數(shù)據(jù)
};
// 為了看效果我模擬的數(shù)據(jù)
const myList = Array.from(Array(1000), (item, index) => ({name: `名字${item}`, id: index}));
const List = (props: ListType) => {
  const {
    itemHeight = 54,
    visibleHeight = 540,
    total = 130,
    dataSource = myList,
  } = props;
  const [showData, setShowData] = useState<any>([]);
  const [offset, setOffset] = useState<any>({ top: 0, bottom: 0 });
  const visibleCount = Math.ceil(visibleHeight / itemHeight);
  useEffect(() => {
    const list = _.slice(dataSource, 0, visibleCount);
    const bottom = (total - visibleCount) * itemHeight;
    setOffset({ top: 0, bottom });
    setShowData(list);
  }, [dataSource]);
  const onScroll = (event: React.UIEvent<HTMLDivElement>) => {
    const target = event.currentTarget;
    const startIdx = Math.floor(target.scrollTop / itemHeight);
    const endIdx = startIdx + visibleCount;
    setShowData(dataSource.slice(startIdx, endIdx));
    const top = startIdx * itemHeight;
    const bottom = (total - endIdx) * itemHeight;
    setOffset({ top, bottom });
  };
  return (
    <div
      className="virtual"
      style={{
        height: visibleHeight,
        width: "100%",
        overflow: "auto",
        border: "1px solid #d9d9d9",
      }}
      onScroll={onScroll} // 在父元素上添加滾動事件監(jiān)聽
    >
      {/* 可視數(shù)據(jù) 為了滾動數(shù)據(jù)一直在可視區(qū)。加上頂部偏移 */}
      <div style={{ height: visibleHeight, marginTop: offset.top }}>
        {_.map(showData, (item, index: any) => {
          return (
            <div
              style={{
                display: "flex",
                alignItems: "center",
                height: itemHeight,
                borderBottom: "1px solid #d9d9d9",
              }}
              key={index}
            >
              {item.name}
            </div>
          );
        })}
      </div>
      {/* 底部占位 */}
      <div style={{ height: offset.bottom }} />
    </div>
  );
};
export default List;

虛擬Table

虛擬表格和虛擬列表的思路差不多,是虛擬列表的一種特殊形式,通常用于處理大型的表格數(shù)據(jù)。類似于虛擬列表,虛擬表格也只渲染當前可見區(qū)域的表格單元格,以優(yōu)化性能并減少內(nèi)存占用。 在ant design4+的版本,也是給出了虛擬列表的實現(xiàn)方式的,基于‘react-window',大家也可以研究研究。我這里就是根據(jù)ant 提供的api components重寫渲染的數(shù)據(jù);獲取到可視區(qū)起點和終點下標,然后只展示當前可視的數(shù)據(jù)。 思路和上面的列表基本一樣,直接上代碼

import React, { useEffect, useRef, useState } from "react";
import { Table } from "antd";
import _ from "lodash";
type TableType = {
  itemHeight?: number; // 每一項的高度
  visibleHeight?: number; // 可見高度
  total?: number; // 數(shù)據(jù)總數(shù)
  dataSource?: any[]; // 全部數(shù)據(jù)
};
// 為了看效果我模擬的數(shù)據(jù)
const myList = Array.from(Array(1000), (item, index) => ({name: `名字${item}`, id: index}));
const VirtualTable = (props: TableType) => {
  const {
    itemHeight = 54,
    visibleHeight = 540,
    total = 130,
    dataSource = myList,
  } = props;
  const [point, setPoint] = useState<any>([0, 20]);
  const [offset, setOffset] = useState<any>({top:0, bottom: 0 });
  const tabRef = useRef<any>();
  const containRef = useRef<any>();
  const visibleCount = Math.ceil(visibleHeight / itemHeight);
  useEffect(() => {
    const bottom = (total - visibleCount) * itemHeight;
    setOffset({ bottom });
    setPoint([0, visibleCount]);
    const scrollDom = tabRef?.current?.querySelector(".ant-table-body");
    console.log("aaa",scrollDom);
    if (scrollDom) {
      containRef.current = scrollDom;
      containRef.current.addEventListener("scroll", onScroll);
      return () => {
        containRef.current.removeEventListener("scroll", onScroll);
      };
    }
  }, [myList]);
  const onScroll = (e: any) => {
    const startIdx = Math.floor(e?.target?.scrollTop / itemHeight);
    const endIdx = startIdx + visibleCount;
    const bottom = (total - endIdx) * itemHeight;
    const top = startIdx * itemHeight;
    setOffset({top,bottom});
    setPoint([startIdx, endIdx]);
  };
  const columns = [
    { title: "ID", dataIndex: "id", width: 150 },
    { title: "名字", dataIndex: "name", width: 150 },
  ];
  return (
    <Table
      ref={tabRef}
      className="virtual-table"
      pagination={false}
      columns={columns}
      dataSource={dataSource}
      scroll={{ y: visibleHeight }}
      components={{
        body: {
          wrapper: ({ className, children }: any) => {
            return (
              <tbody className={className}>
                {children?.[0]}
                <tr style={{height: offset.top}}/>
                {_.slice(children?.[1], point?.[0], point?.[1])}
                <tr style={{height: offset.bottom}}></tr>
              </tbody>
            );
          },
        },
      }}
    />
  );
};
export default VirtualTable;

在上面的代碼里,用到Ant Design的Table組件中的components.body.wrapper定制表格內(nèi)容區(qū)域的包裝器組件。它的作用是對表格的內(nèi)容進行包裝,并且可以自定義一些顯示邏輯。components.body.wrapper函數(shù)接收一個對象參數(shù),其中包含以下參數(shù):

  • className: 傳遞給tbody標簽的類名。它是一個字符串,包含了tbody標簽的類名,可以用于自定義樣式。
  • children: 表格的內(nèi)容區(qū)域的子元素,即表格的數(shù)據(jù)行和列。

在給定的代碼中,components.body.wrapper函數(shù)接收了一個參數(shù)對象,該對象包含classNamechildren屬性。在函數(shù)內(nèi)部,它會將children分割成三部分:

  • children?.[0]:這是表格的標題行,即表頭部分,對應(yīng)于<thead>標簽。
  • {_.slice(children?.[1], point?.[0], point?.[1])}:這是表格的數(shù)據(jù)行,根據(jù)point的取值進行了切片,只渲染point范圍內(nèi)的數(shù)據(jù)行,對應(yīng)于<tr>標簽。
  • <tr style={{height: offset.bottom}}></tr>:這是底部占位行,用于確保在滾動時能正確顯示表格的底部內(nèi)容,對應(yīng)于<tr>標簽,并通過style設(shè)置高度為offset.bottom。

其中,pointoffset是通過其他邏輯計算得到的,可能是在組件的其他部分定義或使用的變量。

通過自定義components.body.wrapper函數(shù),您可以對表格內(nèi)容進行更加靈活的渲染和定制。在這種情況下,它主要用于實現(xiàn)虛擬表格的功能,只渲染可見區(qū)域的數(shù)據(jù)行,從而優(yōu)化大型表格的性能。

總結(jié)

本文只是實現(xiàn)了在固定每項列表高度的情況下的虛擬列表,現(xiàn)實很多情況是不定高的。這個比定高的復雜,不過原理也是一樣的,多了一步需要計算渲染后的實際高度的步驟。我也只是在項目中遇到了,寫下來記錄方便后續(xù)查看。

以上就是詳解JS如何解決大數(shù)據(jù)下滾動頁面卡頓問題的詳細內(nèi)容,更多關(guān)于JS解決頁面卡頓的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • javascript div 遮罩層封鎖整個頁面

    javascript div 遮罩層封鎖整個頁面

    在客戶端瀏覽器中,可以在某個時機使用javascript把一個div作為遮罩層,來封鎖整個頁面。
    2009-07-07
  • Omi v1.0.2發(fā)布正式支持傳遞javascript表達式

    Omi v1.0.2發(fā)布正式支持傳遞javascript表達式

    這篇文章主要介紹了Omi v1.0.2發(fā)布正式支持傳遞javascript表達式,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2017-03-03
  • Bootstrap Table使用整理(一)

    Bootstrap Table使用整理(一)

    基于 Bootstrap 的 jQuery 表格插件,通過簡單的設(shè)置,就可以擁有強大的單選、多選、排序、分頁,以及編輯、導出、過濾(擴展)等等的功能
    2017-06-06
  • 源碼分析Django的message組件

    源碼分析Django的message組件

    這篇文章主要介紹了源碼分析Django的message組件,本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-05-05
  • JavaScript實現(xiàn)動態(tài)添加Form表單元素的方法示例

    JavaScript實現(xiàn)動態(tài)添加Form表單元素的方法示例

    這篇文章主要介紹了JavaScript實現(xiàn)動態(tài)添加Form表單元素的方法,結(jié)合實例形式分析了javascript表單元素操作相關(guān)函數(shù)使用方法與相關(guān)注意事項,需要的朋友可以參考下
    2017-08-08
  • 詳解PHP后期靜態(tài)綁定分析與應(yīng)用

    詳解PHP后期靜態(tài)綁定分析與應(yīng)用

    這篇文章給大家總結(jié)了PHP后期靜態(tài)綁定分析與應(yīng)用的相關(guān)知識點,對此有興趣的朋友可以學習下。
    2018-03-03
  • JavaScript開發(fā)簡單易懂的Svelte實現(xiàn)原理詳解

    JavaScript開發(fā)簡單易懂的Svelte實現(xiàn)原理詳解

    這篇文章主要為大家介紹了JavaScript開發(fā)簡單易懂的Svelte實現(xiàn)原理的內(nèi)容詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步
    2021-11-11
  • javascript 網(wǎng)站常用的iframe分割

    javascript 網(wǎng)站常用的iframe分割

    就是一個頁面使用兩個iframe來調(diào)用內(nèi)容,實現(xiàn)頁面導航,更容易控制,可控制性好
    2008-06-06
  • JS正則表達式驗證密碼強度

    JS正則表達式驗證密碼強度

    這篇文章主要為大家詳細介紹了JS正則表達式驗證密碼強度,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-03-03
  • 教你使用webpack打包編譯TypeScript代碼

    教你使用webpack打包編譯TypeScript代碼

    TypeScript同樣也可以結(jié)合構(gòu)建工具一起使用,下邊以webpack為例介紹一下如何結(jié)合構(gòu)建工具使用TypeScript,本文分步驟給大家介紹的非常詳細,需要的朋友參考下吧
    2021-06-06

最新評論