React+TS前台项目实战(二十八)-- 首页响应式构建之剩余模块布局

news2024/9/29 23:32:08

文章目录

  • 前言
  • BlockList区块
    • 1. 完整页面效果展示
    • 2. 功能分析
    • 3. 代码+详细注释
    • 4. 使用方式
  • TranctionList交易模块
  • 总结


前言

今天,我们继续完善首页剩余模块的响应式布局交互。通过关注点分离的方法,逐步切割模块至最小单元,并结合React的hook钩子函数,实现按需渲染,避免了渲染浪费,提升页面性能,尤其在实时轮询接口时至关重要。
页面带有banner模块搜索模块搜索下拉结果列表切换语言模块区块模块列表交易模块列表,并且采用实时查询。

BlockList区块

1. 完整页面效果展示

(1)PC端效果
在这里插入图片描述

(2)移动端效果
在这里插入图片描述

2. 功能分析

(1)模块由四部分组成,Title标题块、List列表块、Item单位循环块、More跳转更多块组成
(2)结合使用react-query库,实现接口定时轮训
(3)使用memo钩子函数,通过条件比较,看是否重新渲染
(4)使用全局封装useParsedDate方法转换时间戳,BigNumber库转换数字
(5)使用HighLightLink公共组件实现跳转,以及复制功能
(6)使用国际化语言

3. 代码+详细注释

(1)最小项Item组件封装

// @/pages/Home/BlockList/Item/index.tsx
/**
 * 该组件用于显示循环体单个区块信息
 * @param block: 区块信息
 * @param isDelayBlock: 是否为延时区块,延时区块显示不同样式
 */
import { FC, memo } from "react";
import { useTranslation } from "react-i18next";
import BigNumber from "bignumber.js";
import { Block } from "@/models/Block";
import { HighLightLink } from "@/components/HighLightLink";
import { shannonToCkb } from "@/utils";
import { useParsedDate } from "@/hooks/index";
import classNames from "classnames";
import { BlockCard, BlockCardLeft, BlockCardRight, BlockCardCenter } from "./styled";
const _BlockCardItem: FC<{ block: Block; isDelayBlock?: boolean }> = ({ block, isDelayBlock }) => {
  const { t } = useTranslation();
  const liveCellChanges = Number(block.liveCellChanges);
  const [int, dec] = new BigNumber(shannonToCkb(block.reward)).toFormat(2, BigNumber.ROUND_FLOOR).split(".");
  const parsedBlockCreateAt = useParsedDate(block.timestamp);

  return (
    <BlockCard>
      {/* 左侧:区块编号和时间 */}
      <BlockCardLeft>
        <div className={classNames("block-card-number")}>
          <span>#</span>
          <HighLightLink value={block.number.toString()} to={`/block/${block.number}`} />
        </div>
        <span className="block-card-time">{parsedBlockCreateAt}</span>
      </BlockCardLeft>
      {/* 中间:矿工和奖励 */}
      <BlockCardCenter>
        <div className={classNames("block-card-miner")}>
          <span className="block-card-miner">{t("home.miner")}</span>
          <div>{block.minerHash}</div>
        </div>
        <div className={classNames("block-card-reward")}>
          <span>{`${t("home.reward")}`}</span>
          <div className={classNames("reward")}>
            <span data-role="int">{int}</span>
            {dec ? <span data-role="dec" className="monospace">{`.${dec}`}</span> : null}
            {isDelayBlock ? <span data-role="suffix">+</span> : null}
          </div>
        </div>
      </BlockCardCenter>
      {/* 右侧:交易数和单元数 */}
      <BlockCardRight>
        <span className={classNames("block-card-trac-count")}>{`4 TXs`}</span>
        <span className={classNames("block-card-cells")}>{`${liveCellChanges >= 0 ? "+" : "-"}6 ${t("home.cells")}`}</span>
      </BlockCardRight>
    </BlockCard>
  );
};
// 使用memo优化,通过两个条件的比较,当前后两条数据不一致时,重新渲染单个循环体
export const BlockCardItem = memo(_BlockCardItem, (a, b) => a.block.number === b.block.number && a.isDelayBlock === b.isDelayBlock);
--------------------------------------------------------------------------------------------------------------
// @/pages/Home/BlockList/Item/styled.tsx
import styled from "styled-components";
export const BlockCard = styled.div`
  display: flex;
  align-items: center;
  padding: 13px;
  background: #fff;
`;
export const BlockCardLeft = styled.div`
  flex: 2.2;
  min-width: 0;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  .block-card-number {
    display: flex;
    align-items: center;
    font-size: 13px;
    span {
      margin-right: 3px;
    }
  }
  .block-card-time {
    color: var(--cd-gray-light-3);
    font-size: 12px;
    margin-top: 10px;
  }
`;
export const BlockCardCenter = styled.div`
  flex: 3.8;
  min-width: 0;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  .block-card-miner {
    display: flex;
    align-items: center;
    font-size: 13px;
    margin-right: 10px;
    span {
      margin-right: 3px;
    }
  }
  .block-card-reward {
    display: flex;
    align-items: center;
    color: var(--cd-gray-light-3);
    margin-top: 10px;
    .reward {
      display: flex;
      align-items: center;
      margin-left: 10px;
      span[data-role="int"] {
        font-size: 14px;
      }

      span[data-role="dec"] {
        font-size: 9px;
        padding-right: 2px;
      }

      span[data-role="suffix"] {
        font-size: 12px;
      }
    }
  }
`;
export const BlockCardRight = styled.div`
  flex: 1.2;
  min-width: 0;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  .block-card-trac-count {
    display: flex;
    align-items: center;
    font-size: 13px;
  }
  .block-card-cells {
    font-size: 12px;
    color: var(--cd-gray-light-3);
    margin-top: 10px;
  }
`;

(2)List模块,引入Item组件循环数据

 const List: FC = () => {
    return blocks.length > 0 ? (
      <>
        {blocks.map((item, index) => (
          <div key={item.number}>
            <BlockCardItem block={item} isDelayBlock={index < DELAY_BLOCK_NUMBER} />
            {blocks.length - 1 !== index && <div className="blockCardSeparate" />}
          </div>
        ))}
      </>
    ) : (
      <Loading />
    );
  };

(3)BlockList父组件

import { FC, memo } from "react";
import { useNavigate } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { BlockCardItem } from "./Item";
import Loading from "@/components/Loading";
import { Block } from "@/models/Block";
import { DELAY_BLOCK_NUMBER } from "@/global/constants/common";
import { BlockListContainer, BlockListTitle } from "./styled";
import { MoreButton } from "@/pages/Home/styled";
const BlockList: FC<{ blocks: Block[] }> = memo(({ blocks }) => {
  const {
    t,
    i18n: { language },
  } = useTranslation();
  const navigate = useNavigate();
  // 区块列表
  const List: FC = () => {
    return blocks.length > 0 ? (
      <>
        {blocks.map((item, index) => (
          <div key={item.number}>
            {/* 单个区块 */}
            <BlockCardItem block={item} isDelayBlock={index < DELAY_BLOCK_NUMBER} />
            {blocks.length - 1 !== index && <div className="blockCardSeparate" />}
          </div>
        ))}
      </>
    ) : (
      <Loading />
    );
  };
  return (
    // 区块列表容器
    <BlockListContainer>
      {/* 区块列表标题 */}
      <BlockListTitle>{t("home.latest_blocks")}</BlockListTitle>
      {/* 区块列表 */}
      <List />
      {/* 查看更多按钮 */}
      <MoreButton
        onClick={() => {
          navigate(`/${language}/block/list`);
        }}
      >
        <span>{t("home.more")}</span>
      </MoreButton>
    </BlockListContainer>
  );
});
export default BlockList;
--------------------------------------------------------------------------------------------------------------
import styled from "styled-components";
export const BlockListContainer = styled.div`
  flex: 1;
  width: 100%;
  min-width: 0;
  margin: 0 20px 40px 0;
  border-radius: 6px;
  overflow: hidden;
  bacnground: #fff;
  box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.1);
`;
export const BlockListTitle = styled.div`
  width: 100%;
  height: 40px;
  line-height: 40px;
  font-size: 16px;
  font-weight: 600;
  color: #fff;
  padding-left: 12px;
  background: var(--cd-primary-color);
`;

4. 使用方式

// 引入
import BlockList from "./BlockList";
// 数据模拟,下一节对接真实接口,轮训处理
const blocks = [
  {
    miner_hash: "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsq0tpsqq08mkay9ewrfrdwlcghv62qw704s93hhsj",
    number: "13418010",
    timestamp: "1720282004389",
    reward: "57350724258",
    transactions_count: "1",
    live_cell_changes: "1",
  },
  // ...
];
// 使用
<BlockList blocks={blocks} />

TranctionList交易模块

这块其实与BlockList模块布局相似,都差不多的,大家可以尝试着拆分块的方式,尝试开发

总结

下一篇讲【首页结合react-query库实现数据对接】。关注本栏目,将实时更新。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1913597.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

中职大数据实训室

一、中职大数据实训室建设背景 《中华人民共和国国民经济和社会发展第十四个五年规划和2035年远景目标纲要》中强调了数字化转型的重要性&#xff0c;明确提出了建设数字中国的战略目标。大数据技术作为数字化转型的核心&#xff0c;对于培养具备大数据分析、处理和应用能力的…

K8s GPU 资源管理探索:在 KubeSphere 上部署 AI 大模型 Ollama

作者&#xff1a;运维有术星主 随着人工智能、机器学习、AI 大模型技术的迅猛发展&#xff0c;我们对计算资源的需求也在不断攀升。特别是对于需要处理大规模数据和复杂算法的 AI 大模型&#xff0c;GPU 资源的使用变得至关重要。对于运维工程师而言&#xff0c;掌握如何在 Kub…

人工智能和机器学习 (复旦大学计算机科学与技术实践工作站)20240703(上午场)人工智能初步、mind+人脸识别

前言 在这个科技日新月异的时代&#xff0c;人工智能&#xff08;AI&#xff09;已经逐渐渗透到我们生活的方方面面&#xff0c;从智能家居到自动驾驶&#xff0c;无一不彰显着AI的强大潜力。而人脸识别技术作为AI领域的一项重要应用&#xff0c;更是以其高效、便捷的特点受到了…

[C++初阶]list类的初步理解

一、标准库的list类 list的底层是一个带哨兵位的双向循环链表结构 对比forward_list的单链表结构&#xff0c;list的迭代器是一个双向迭代器 与vector等顺序结构的容器相比&#xff0c;list在任意位置进行插入删除的效率更好&#xff0c;但是不支持任意位置的随机访问 list是一…

最新Wireshark查看包中gzip内容

虽然是很简单的事情&#xff0c;但是网上查到的查看gzip内容的方法基本都是保存成zip文件&#xff0c;然后进行二进制处理。 其实现在最新版本的Wireshark已经支持获取gzip内容了。 选中HTTP协议&#xff0c;右键选择[追踪流]->[HTTP Stream] 在弹出窗口中&#xff0c;已…

图像分割SAM辅助标注工具,可调用SAM等大模型(保姆级教程)

SAM等模型通过先进的深度学习技术&#xff0c;实现了高效、精准的图像自动化标注。这不仅显著降低了人工标注的工作量和成本&#xff0c;提高了标注效率和精度&#xff0c;还为各个领域的研究和应用提供了强大的技术支持。随着SAM等模型的不断完善和应用&#xff0c;自动化标注…

一个项目学习Vue3---Vue3中自带的事件

1. .stop 阻止事件继续传播&#xff0c;即防止事件冒泡到父元素。 <div click.stop"handleClick">点击我</div> 2. .prevent 阻止事件的默认行为&#xff0c;比如阻止表单提交时的页面刷新。 <form submit.prevent"handleSubmit">阻…

springboot美食分享平台-计算机毕业设计源码45429

基于Web美食分享平台的系统设计与实现 摘 要 本研究基于Spring Boot框架&#xff0c;设计并实现了一个Web美食分享平台&#xff0c;旨在为用户提供一个交流分享美食体验的社区平台。该平台涵盖了用户注册登录、美食制作方法分享发布、点赞评论互动等功能模块&#xff0c;致力于…

递归(四)—— 初识暴力递归之“打印字符串的全排列”

题目1&#xff1a;序列打印一个字符串的全排列 题目分析&#xff1a;结合一实例来理解题目&#xff0c;str “abc”的全排列就是所求得的序列是 strp[0~2]的所有位的排列组合&#xff0c;strNew {“abc”, “acb”, “bac”, “bca”,”cba”,”cab”} 思路1&#xff1a;枚…

外卖小哥必备!高性价比千元机,送餐更高效

有一群赶时间的人&#xff0c;经常看到他们慌忙的穿梭于大街小巷&#xff0c;他们不仅是城市的风景线&#xff0c;更是无数人心中的温暖使者——外卖小哥 在争分夺秒的背后&#xff0c;一台合适的手机&#xff0c;成为了他们不可或缺的必需品&#xff0c;那什么样的手机更方便呢…

从两眼放光到心碎一地《长相思》第二季搞笑爱情转折

这《长相思》第二季的剧情&#xff0c; 简直是心脏按摩器升级版啊&#xff01; 爷爷一开口&#xff0c;要给玱玹安排馨悦当王后 我这小心脏差点就跟着‘嘭’一声 "哎呀&#xff0c;以为要上演宫廷版《速度与激情》 结果小夭女神一出手&#xff0c; 不是醋坛子翻&#…

Ubuntu 20版本安装Redis教程,以及登陆

第一步 切换到root用户&#xff0c;使用su命令&#xff0c;进行切换。 输入&#xff1a; su - 第二步 使用apt命令来搜索redis的软件包&#xff0c;输入命令&#xff1a;apt search redis 第三步 选择需要的redis版本进行安装&#xff0c;本次选择默认版本&#xff0c;redis5.…

Redis基础教程(十九):Redis分区

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…

79 单词搜索

题目 给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 单词必须按照字母顺序&#xff0c;通过相邻的单元格内的字母构成&#xff0c;其中“相邻”单元格是那些水平相邻或…

视频短信群发平台的显著优势

视频短信&#xff0c;这一融合了视频、音频与文本的创新通信方式&#xff0c;不仅革新了传统短信的单一形式&#xff0c;更以其独特的魅力引领着移动通信的新风尚。它以移动视频格式&#xff08;如3GP、MP4&#xff09;为载体&#xff0c;通过GPRS网络和WAP无线应用协议&#x…

操作系统之进程控制

进程 一、进程创建1、进程2、fork函数&#xff08;1&#xff09;概念&#xff08;2&#xff09;创建进程执行到内核中的fork代码时&#xff0c;内核做的操作&#xff08;3&#xff09;返回值 3、常规用法4、代码5、执行结果 二、进程终止1、进程的退出结果2、常见退出方法&…

Transformer中的编码器和解码器结构有什么不同?

Transformer背后的核心概念&#xff1a;注意力机制&#xff1b;编码器-解码器结构&#xff1b;多头注意力等&#xff1b; 例如&#xff1a;The cat sat on the mat&#xff1b; 1、嵌入&#xff1a; 首先&#xff0c;模型将输入序列中的每个单词嵌入到一个高维向量中表示&…

JavaScript包管理器:yarn的安装与配置详解

Yarn是一个流行的JavaScript包管理器&#xff0c;它允许开发者使用代码来安装、更新和删除项目中的依赖包。Yarn的安装与配置过程相对简单&#xff0c;以下将详细说明这一过程&#xff1a; 一、Yarn的安装 Yarn的安装可以通过多种方式进行&#xff0c;主要取决于你的操作系统…

深入剖析C++的 “属性“(Attribute specifier sequence)

引言 在阅读开源项目源代码是&#xff0c;发现了一个有趣且特殊的C特性&#xff1a;属性。 属性&#xff08;attribute specifier sequences&#xff09;是在C11标准引入的。在C11之前&#xff0c;编译器特有的扩展被广泛用来提供额外的代码信息。例如&#xff0c;GNU编译器&…