React+TS前台项目实战(三十)-- 首页构建之基于react-query和性能hook实现全页面数据渲染


前言

前三篇文章详细介绍了首页的响应式布局,采用关注点分离进行模块拆解,现在只需按需引入模块,页面更加简洁,代码的维护性得到提升。今天将进入首页的收尾阶段,即完成剩余区块列表、交易列表接口数据渲染,轮训,各种钩子使用以提升性能。


一、效果展示

(1)PC端区块列表、交易列表轮询

在这里插入图片描述

(2)PC端搜索查询

在这里插入图片描述

(3)移动端区块列表、交易列表轮询

在这里插入图片描述

(4)移动端搜索查询

在这里插入图片描述

二、首页源码+详细注释说明+技术分析

1. 页面功能分析

(1)页面组成:页面由渲染Banner、搜索块、统计项、哈希速率图表、区块时间图表、区块列表和交易列表等模块组成
(2)数据获取:使用@tanstack/react-query库进行数据获取、缓存、轮训
(3)性能处理:使用React钩子,如useMemo、useTranslation来管理状态、获取数据和国际化,提升性能
(4)自定义钩子使用:使用自定义钩子useStatistics生成并显示统计数据
(5)详细模块封装请看前面三讲首页响应式构建之banner/搜索/统计模块布局首页响应式构建之区块/交易布局优化实现首页Echarts模块数据渲染

2. 代码+详细注释

// @/page/Home/index.tsx
import { FC, useMemo } from "react";
import classNames from "classnames";
import { useTranslation } from "react-i18next";
import { useQuery } from "@tanstack/react-query";
import Content from "@/components/Content";
import Banner from "./Banner";
import SearchBlock from "./SearchBlock";
import HashRateEchart from "./HashRateEchart/index";
import BlockTimeChart from "./BlockTimeChart/index";
import BlockList from "./BlockList";
import TranctionList from "./TranctionList";
import { HomeStatisticBlock, HomeStatisticItem, HomeTableBlock } from "./styled";
import { queryBlocks, queryTransactions } from "@/api/home";
import { BLOCK_POLLING_TIME } from "@/global/constants/common";
import { useStatistics } from "./requester";
const HomePage: FC = () => {
  // 使用useQuery,异步获取最新的区块数据
  const blocksQuery = useQuery(
    ["latest_blocks"],
    async () => {
      const { data } = await queryBlocks({
        page: 1, // 从第一页开始获取
        page_size: 25, // 每页获取25个区块
      });
      // 将获取到的区块数据转换为我们需要的格式
      const blocks = data.map((wrapper: any) => {
        return {
          ...wrapper.attributes,
        };
      });
      return {
        blocks, // 最新的区块列表
        total: data?.length, // 最新区块的总数
      };
    },
    {
      refetchInterval: BLOCK_POLLING_TIME, // 每隔一段时间重新获取最新的区块数据
    }
  );
  // 使用useMemo钩子,blocksQuery数据变化时,重新获取
  const blocks = useMemo(() => blocksQuery.data?.blocks.slice(0, 10) ?? [], [blocksQuery.data?.blocks]);
  // 使用useQuery,异步获取最新的交易数据
  const transactionsQuery = useQuery(
    ["latest_transactions"],
    async () => {
      const { data } = await queryTransactions({
        page: 1,
        page_size: 25,
      });
      const transactions = data.map((wrapper: any) => {
        return {
          ...wrapper.attributes,
        };
      });
      // 将获取到的交易数据转换为我们需要的格式
      return {
        transactions,
        total: transactions?.length,
      };
    },
    {
      refetchInterval: BLOCK_POLLING_TIME,
    }
  );
  // 使用useMemo钩子,transactionsQuery数据变化时,重新获取
  const transactions = useMemo(() => transactionsQuery.data?.transactions.slice(0, 10) ?? [], [transactionsQuery.data?.transactions]);

  // 定义统计数据类型声明
  interface EchartsAndData {
    name: string;
    value: string;
  }
  const useEchartsAndDataList = (): EchartsAndData[] => {
    const { t } = useTranslation();
    const statistics = useStatistics();
    return [
      {
        name: t("home.echartsAndData.name1"),
        value: t(statistics.value1),
      },
      {
        name: t("home.echartsAndData.name2"),
        value: t(statistics.value2),
      },
      {
        name: t("home.echartsAndData.name3"),
        value: t(statistics.value3),
      },
      {
        name: t("home.echartsAndData.name4"),
        value: t(statistics.value4),
      },
    ];
  };
  // 使用区块链统计数据
  const echartsAndDataList = useEchartsAndDataList();
  // 统计数据渲染结构体
  const StatisticItem = ({ data }: { data: EchartsAndData }) => (
    <HomeStatisticItem>
      <div className={classNames("statistic-item-left-title")}>{data.name}</div>
      <div className={classNames("statistic-item-left-value")}>{data.value}</div>
    </HomeStatisticItem>
  );
  return (
    <Content>
      {/* Banner模块 */}
      <Banner />
      <div className="container">
        {/* 搜索模块 */}
        <SearchBlock />
        {/* echarts图表以及统计数据模块 */}
        <HomeStatisticBlock>
          <div className={classNames("statistic-item")}>
            <div className={classNames("statistic-item-left")}>
              {/* 统计数据 */}
              <StatisticItem data={echartsAndDataList[0]}></StatisticItem>
              <StatisticItem data={echartsAndDataList[1]}></StatisticItem>
            </div>
            <div className={classNames("statistic-item-right")}>
              {/* 统计图表 */}
              <HashRateEchart />
            </div>
          </div>
          <div className={classNames("statistic-item")}>
            <div className={classNames("statistic-item-left")}>
              {/* 统计数据 */}
              <StatisticItem data={echartsAndDataList[2]}></StatisticItem>
              <StatisticItem data={echartsAndDataList[3]}></StatisticItem>
            </div>
            <div className={classNames("statistic-item-right")}>
              {/* 统计图表 */}
              <BlockTimeChart />
            </div>
          </div>
        </HomeStatisticBlock>
      </div>
      <HomeTableBlock className="container">
        {/* 区块列表模块 */}
        <BlockList blocks={blocks} />
        {/* 交易列表模块 */}
        <TranctionList transactions={transactions} />
      </HomeTableBlock>
    </Content>
  );
};
export default HomePage;
----------------------------------------------------------------------------------------------------
// @/pages/Home/styled.tsx
import styled from "styled-components";
import variables from "@/styles/variables.module.scss";
export const HomeStatisticBlock = styled.div`
  width: 100%;
  height: 207px;
  display: flex;
  margin-bottom: 20px;
  .statistic-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex: 1;
    background: #232323;
    @media (max-width: ${variables.extraLargeBreakPoint}) {
      flex-direction: column;
    }
    @media (max-width: ${variables.mobileBreakPoint}) {
    }
    .statistic-item-left {
      flex: 1;
      width: 100%;
      height: 100%;
    }
    .statistic-item-right {
      flex: 2;
      width: 100%;
      height: 100%;
      padding: 10px;
      // background: linear-gradient(304deg, #6e85e0 2%, #577cdb 48%, #486ecc 99%);
      @media (max-width: ${variables.extraLargeBreakPoint}) {
      }
      @media (max-width: ${variables.mobileBreakPoint}) {
      }
    }
    &:last-child {
      background: #484e4e;
    }
  }
  @media (max-width: ${variables.extraLargeBreakPoint}) {
    height: 310px;
  }
  @media (max-width: ${variables.mobileBreakPoint}) {
    flex-direction: column;
    height: auto;
  }
`;
export const HomeStatisticItem = styled.div`
  padding: 30px;
  display: flex;
  justify-content: space-between;
  flex-direction: column;
  color: #fff;
  .statistic-item-left-title {
    font-size: 14px;
    margin-bottom: 5px;
  }
  .statistic-item-left-value {
    font-size: 18px;
    font-weight: bold;
  }
  @media (max-width: ${variables.extraLargeBreakPoint}) {
    padding: 15px 30px;
    flex-direction: row;
    margin-bottom: 0;
    .statistic-item-left-value {
      font-size: 16px;
    }
  }
  @media (max-width: ${variables.mobileBreakPoint}) {
    padding: 10px 20px;
  }
`;
export const HomeTableBlock = styled.div`
  display: flex;
  @media (max-width: ${variables.extraLargeBreakPoint}) {
  }
  @media (max-width: ${variables.mobileBreakPoint}) {
    flex-direction: column;
  }
`;

总结

有人可能会说频繁轮询接口会不会影响性能,当然会,但是我们做了一些措施,可以有效地优化性能。首先,我们使用了useMemo和memo等性能钩子函数,就是为了避免重复渲染。同时,使用useQuery处理请求时,它提供了一些性能优化的功能,比如自动缓存和懒加载。关注本栏目,会实时更新。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值