React+TS前台项目实战(二十六)-- 高性能可配置Echarts图表组件封装

文章目录

  • 前言
  • CommonChart组件
    • 1. 功能分析
    • 2. 代码+详细注释
    • 3. 使用到的全局hook代码
    • 4. 使用方式
    • 5. 效果展示
  • 总结


前言

Echarts图表在项目中经常用到,然而,重复编写初始化,更新,以及清除实例等动作对于开发人员来说是一种浪费时间和精力。因此,在这篇文章中,将封装一个 “高性能可配置Echarts组件” ,简化开发的工作流程,提高数据可视化的效率和质量。

CommonChart组件

1. 功能分析

(1)可以渲染多种类型的图表,包括折线图、柱状图、饼图、地图和散点图
(2)通过传入的 option 属性,配置图表的各种参数和样式
(4)通过传入的 onClick 属性,处理图表元素的点击事件
(5)通过传入的 notMerge 属性,控制是否合并图表配置
(6)通过传入的 lazyUpdate 属性,控制是否懒渲染图表
(7)通过传入的 style 属性,设置图表容器的样式
(8)通过传入的 className 属性,自定义图表容器的额外类名
(9)通过监听窗口大小变化,自动调整图表的大小
(10)使用 usePrevious、useWindowResize 和 useEffect 等钩子来提高组件性能并避免不必要的渲染

2. 代码+详细注释

// @/components/Echarts/commom/index.tsx
import { useRef, useEffect, CSSProperties } from "react";
// 引入 Echarts 的各种图表组件和组件配置,后续备用
import "echarts/lib/chart/line"; // 折线图
import "echarts/lib/chart/bar"; // 柱状图
import "echarts/lib/chart/pie"; // 饼图
import "echarts/lib/chart/map"; // 地图
import "echarts/lib/chart/scatter"; // 散点图
import "echarts/lib/component/tooltip"; // 提示框组件
import "echarts/lib/component/title"; // 标题组件
import "echarts/lib/component/legend"; // 图例组件
import "echarts/lib/component/markLine"; // 标线组件
import "echarts/lib/component/dataZoom"; // 数据区域缩放组件
import "echarts/lib/component/brush"; // 刷选组件
// 引入 Echarts 的类型声明
import * as echarts from "echarts";
import { ECharts, EChartOption } from "echarts";
// 引入自定义的钩子函数和公共函数
import { useWindowResize, usePrevious } from "@/hooks";
import { isDeepEqual } from "@/utils";
/*** 公共 Echarts 业务灵巧组件,可在项目中重复使用** @param {Object} props - 组件属性* @param {EChartOption} props.option - Echarts 配置项* @param {Function} [props.onClick] - 点击事件处理函数* @param {boolean} [props.notMerge=false] - 是否不合并数据* @param {boolean} [props.lazyUpdate=false] - 是否懒渲染* @param {CSSProperties} [props.style] - 组件样式* @param {string} [props.className] - 组件类名* @returns {JSX.Element} - React 组件*/
type Props = {option: EChartOption;onClick?: (param: echarts.CallbackDataParams) => void;notMerge?: boolean;lazyUpdate?: boolean;style?: CSSProperties;className?: string;
};
const CommonChart = (props: Props) => {// 解构属性,并设置默认值const {option,onClick, // 点击事件处理函数notMerge = false, // 是否不合并数据,默认为 falselazyUpdate = false, // 是否懒渲染,默认为 falsestyle, // 组件样式className = "", // 组件类名,默认为空字符串} = props;// 创建 ref 来引用 div 元素,并初始化 chartInstanceRef 为 nullconst chartRef = useRef<HTMLDivElement>(null);const chartInstanceRef = useRef<ECharts | null>(null);// 使用 usePrevious 钩子函数来记录上一次的 option 和 onClick 值const prevOption = usePrevious(option);const prevClickEvent = usePrevious(onClick);useEffect(() => {// 定义一个变量来存储图表实例let chartInstance: ECharts | null = null;if (chartRef.current) {// 如果图表实例不存在,则初始化if (!chartInstanceRef.current) {const hasRenderInstance = echarts.getInstanceByDom(chartRef.current);if (hasRenderInstance) {hasRenderInstance.dispose();}chartInstanceRef.current = echarts.init(chartRef.current);}// 暂存当前的图表实例chartInstance = chartInstanceRef.current;// 如果 option 或 onClick 值发生变化,则重新渲染try {if (!isDeepEqual(prevOption, option, ["formatter"])) {chartInstance.setOption(option, { notMerge, lazyUpdate });}if (onClick && typeof onClick === "function" && onClick !== prevClickEvent) {chartInstance.on("click", onClick);}} catch (error) {chartInstance && chartInstance.dispose();}}}, [option, onClick, notMerge, lazyUpdate, prevOption, prevClickEvent]);// 监听窗口大小变化,当窗口大小变化时,重新渲染图表useWindowResize(() => {if (chartInstanceRef.current) {chartInstanceRef.current?.resize();}});return <div style={{ ...style }} className={className} ref={chartRef}></div>;
};export { CommonChart };

3. 使用到的全局hook代码

// @/utils/index
// 深度判断两个对象某个属性的值是否相等
export const isDeepEqual = (left: any, right: any, ignoredKeys?: string[]): boolean => {const equal = (a: any, b: any): boolean => {if (a === b) return trueif (a && b && typeof a === 'object' && typeof b === 'object') {if (a.constructor !== b.constructor) return falselet lengthlet iif (Array.isArray(a)) {length = a.lengthif (length !== b.length) return falsefor (i = length; i-- !== 0;) {if (!equal(a[i], b[i])) return false}return true}if (a instanceof Map && b instanceof Map) {if (a.size !== b.size) return falsefor (i of a.entries()) {if (!b.has(i[0])) return false}for (i of a.entries()) {if (!equal(i[1], b.get(i[0]))) return false}return true}if (a instanceof Set && b instanceof Set) {if (a.size !== b.size) return falsefor (i of a.entries()) if (!b.has(i[0])) return falsereturn true}if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flagsif (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf()if (a.toString !== Object.prototype.toString) return a.toString() === b.toString()const keys = Object.keys(a)length = keys.lengthif (length !== Object.keys(b).length) return falsefor (i = length; i-- !== 0;) {if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false}for (i = length; i-- !== 0;) {const key = keys[i]if (key === '_owner' && a.$$typeof) {// Reactcontinue}if (ignoredKeys && ignoredKeys.includes(key)) {continue}if (!equal(a[key], b[key])) return false}return true}// eslint-disable-next-line no-self-comparereturn a !== a && b !== b}return equal(left, right)
}
--------------------------------------------------------------------------
// @/hooks/index.ts
/*** Returns the value of the argument from the previous render* @param {T} value* @returns {T | undefined} previous value* @see https://react-hooks-library.vercel.app/core/usePrevious*/
export function usePrevious<T>(value: T): T | undefined {const ref = useRef<T>()useEffect(() => {ref.current = value}, [value])return ref.current
}export function useWindowResize(callback: (event: UIEvent) => void) {useEffect(() => {window.addEventListener('resize', callback)return () => window.removeEventListener('resize', callback)}, [callback])
}

4. 使用方式

// 引入组件和echarts
import { CommonChart } from "@/components/Echarts/common";
import echarts from "echarts/lib/echarts";
// 使用
const useOption = () => {return (data: any): echarts.EChartOption => {return {color: ["#ffffff"],title: {text: "图表y轴时间",textAlign: "left",textStyle: {color: "#ffffff",fontSize: 12,fontWeight: "lighter",fontFamily: "Lato",},},grid: {left: "2%",right: "3%",top: "15%",bottom: "2%",containLabel: true,},xAxis: [{axisLine: {lineStyle: {color: "#ffffff",width: 1,},},data: data.map((item: any) => item.xTime),axisLabel: {formatter: (value: string) => value,},boundaryGap: false,},],yAxis: [{position: "left",type: "value",scale: true,axisLine: {lineStyle: {color: "#ffffff",width: 1,},},splitLine: {lineStyle: {color: "#ffffff",width: 0.5,opacity: 0.2,},},axisLabel: {formatter: (value: string) => new BigNumber(value),},boundaryGap: ["5%", "2%"],},{position: "right",type: "value",axisLine: {lineStyle: {color: "#ffffff",width: 1,},},},],series: [{name: t("block.hash_rate"),type: "line",yAxisIndex: 0,lineStyle: {color: "#ffffff",width: 1,},symbol: "none",data: data.map((item: any) => new BigNumber(item.yValue).toNumber()),},],};};
};
const echartData = [{ xTime: "2020-01-01", yValue: "1500" },{ xTime: "2020-01-02", yValue: "5220" },{ xTime: "2020-01-03", yValue: "4000" },{ xTime: "2020-01-04", yValue: "3500" },{ xTime: "2020-01-05", yValue: "7800" },
];
const parseOption = useOption();
<CommonChartoption={parseOption(echartData, true)}notMergelazyUpdatestyle={{height: "180px",}}
></ChartBlock>

5. 效果展示

在这里插入图片描述


总结

下一篇讲【首页响应式搭建以及真实数据渲染】。关注本栏目,将实时更新。

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

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

相关文章

LVS-DR负载均衡

LVS-DR负载均衡 LVS—DR工作模式 原理 客户端访问调度器的VIP地址&#xff0c;在路由器上应该设置VIP跟调度器的一对一的映射关系&#xff0c;调度器根据调度算法将该请求“调度“到后端真实服务器&#xff0c;真实服务器处理完毕后直接将处理后的应答报文发送给路由器&#xf…

EDI安全:如何在2024年保护您的数据免受安全和隐私威胁

电子数据交换&#xff08;EDI&#xff09;支持使用标准化格式在组织之间自动交换业务文档。这种数字化转型彻底改变了业务通信&#xff0c;消除了对纸质交易的需求并加速了交易。然而&#xff0c;随着越来越依赖 EDI 来传输发票、采购订单和发货通知等敏感数据&#xff0c;EDI …

【跨境分享】中国商家如何卷到国外?电商独立站和电商平台的优势对比

为什么要选择独立站而不是电商平台 对于跨境电商经营者而言&#xff0c;采取多平台、多站点的运营策略是至关重要的战略布局。这一做法不仅有助于分散风险&#xff0c;避免将所有投资集中于单一市场&#xff0c;从而降低“所有鸡蛋置于同一篮子”的隐患&#xff0c;而且有利于拓…

【友邦保险-注册安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…

华为od相关信息分享

2024年OD统一考试&#xff08;D卷&#xff09;完整题库&#xff1a;华为OD机试2024年最新题库&#xff08;Python、JAVA、C合集&#xff09; 问 1.什么是华为od&#xff1f; 答&#xff1a;OD全称是Outsourcing Dispacth&#xff0c;即外包派遣&#xff0c;是华为和外企德科…

金蝶云苍穹-插件开发(三)关于基础资料/单据实体的id

基础资料/单据实体的id 每个基础资料和单据的实体&#xff0c;都有一个id字段&#xff0c;这个id是其一个唯一性标识&#xff0c;就类似于这个实体的身份证号一样。通常&#xff0c;这个id用来找到指定的实体。这个id在代码中都是long类型接收的。 如果基础资料/单据里面有字…

Kafka日志处理:深入了解偏移量查找与切分文件

我是小米,一个喜欢分享技术的29岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货! Hello, 大家好!我是你们的技术小伙伴小米,今天要和大家分享一些关于Kafka日志处理的深入知识。我们将讨论如何查看偏移量为23的消息,以及Kafka日志分…

重载、覆盖(重写)、重定义(同名隐藏)的区别 (超详解)

&#x1f4da; 重载&#xff08;Overloading&#xff09;、覆盖&#xff08;Overriding&#xff09;、重定义&#xff08;Hiding&#xff09;是面向对象编程中常见的概念&#xff0c;它们分别用于描述不同情况下函数或方法的行为。 目录 重载&#xff08;Overloading&#xff…

ST7789 linux4.x驱动

文章目录 ST7789 linux4.x驱动设备树配置驱动程序编译驱动测试驱动 ST7789 linux4.x驱动 设备树配置 pinctrl_ecspi2_cs_1: ecspi2_cs_grp-1 {fsl,pins <MX6UL_PAD_CSI_DATA01__GPIO4_IO22 0x40017059>; };pinctrl_ecspi2_1: escpi2grp {fsl,pins <MX6UL_PAD_CSI_…

RocketMQ快速入门:主从、集群模式原理及搭建实操(十一)

目录 0. 引言1. 前备知识1.1 namesrv集群模式1.2 broker集群模式1.2 broker主从复制原理 2. 集群部署2.1 环境准备2.2 配置讲解2.3 一主多从模式部署2.4 多主无从模式部署2.5 多主多从模式部署 3. 总结 0. 引言 在学习完rocketmq的基础知识后&#xff0c;我们进入rocketmq高可…

痉挛性斜颈对生活有哪些影响?

痉挛性斜颈&#xff0c;这个名字听起来可能并不熟悉&#xff0c;但它实际上是一种神经系统疾病&#xff0c;影响着全球数百万人的生活质量。它以一种无法控制的方式&#xff0c;使患者的颈部肌肉发生不自主的收缩&#xff0c;导致头部姿势异常。对于患者来说&#xff0c;痉挛性…

和Bug较劲的第n天:[Error: Unable to open snapshot file: No such file or directory]

问题描述 最近做了一个小demo&#xff0c;基于parcel的&#xff0c;在迁移仓库的时候发生了一个报错 [Error: Unable to open snapshot file: No such file or directory] 原因分析&#xff1a; 在迁移仓库的时候&#xff0c;我将项目放入了一个以中文命名的文件夹里&#xf…

模电基础 - 信号的运算和处理

目录 一. 简介 二. 加法 三. 减法 四. 乘法 五. 除法 六. 总结 一. 简介 在模电基础中&#xff0c;信号的运算和处理是非常重要的内容。 信号的运算包括加法、减法、乘法、除法等。通过使用集成运放&#xff0c;可以很容易地实现这些运算。例如&#xff0c;利用反相输入…

算法的几种常见形式

算法&#xff08;Algorithm&#xff09; 算法&#xff08;Algorithm&#xff09;是指解决问题或完成任务的一系列明确的步骤或规则。在计算机科学中&#xff0c;算法是程序的核心部分&#xff0c;它定义了如何执行特定的任务或解决特定的问题。算法可以用多种方式来表示和实现…

宜春旅游集散中心展厅OLED透明屏方案设计

一、项目概述 为提升宜春旅游集散中心展厅的现代化展示水平&#xff0c;增强游客的参观体验&#xff0c;我们计划在展厅的核心区域引入OLED透明屏技术。该方案旨在通过高科技的视觉呈现方式&#xff0c;将展品信息以虚拟与现实相结合的方式展现&#xff0c;打造出一个既具科技感…

谷粒商城学习笔记-22-分布式组件-SpringCloud-OpenFeign测试远程调用

文章目录 一&#xff0c;OpenFeign的简介二&#xff0c;OpenFeign的使用步骤1&#xff0c;场景说明2&#xff0c;引入依赖2&#xff0c;开启OpenFeign3&#xff0c;编写Feign接口4&#xff0c;使用feign调用远程接口5&#xff0c;验证 错误记录 上一节学习了注册中心&#xff0…

鼠标录制工具|键鼠轨迹录制,实现自动办公

利用键鼠录制工具录制固定的鼠标点击、键盘输入等操作&#xff0c;实现自动化执行固定操作&#xff0c;节省时间。鼠标录制功能可以录制多步骤的操作&#xff0c;将录制的动作保存并命名&#xff0c;甚至可以编辑操作速度。下面将演示几种生活中常见的案例&#xff0c;详细讲解…

企业微信hook接口协议,移除群成员通知

移除群成员通知 返回示例 {"flag": 0, "receiver": 0, "sender_name": "", "is_room": 1, "server_id": 15318083, "send_time": 1687688952, "sender": 1688855749266556, "referid&…

k8s中使用cert-manager生成自签名证书

一、安装 cert-manager 注意查看cert-manager和K8S支持的对应版本 我的 k8sv1.28.2&#xff0c;cert-manager v1.12.11 下载 cert-manager.yaml 文件&#xff0c;执行 kubectl apply -f cert-manager.yaml二、生成自签名证书 cert-selfsigned.yaml apiVersion: cert-manage…

【SpringBoot】随机盐值+双重SHA256加密实战

目录&#xff1a; 1.SHA-256和随机盐值 2.前端实现 3.后端实现 1.SHA-256和Salt 1.1.什么是SHA-256 SHA-256是一种信息摘要算法&#xff0c;也是一种密码散列函数。对于任意长度的消息&#xff0c;SHA256都会产生一个256bit长的散列值&#xff08;哈希值&#xff09;&…