首页 > web前端 > js教程 > 正文

React 中 ECharts 多实例窗口调整大小失效的解决方案

碧海醫心
发布: 2025-11-05 16:58:12
原创
1001人浏览过

React 中 ECharts 多实例窗口调整大小失效的解决方案

react 应用中渲染多个 echarts 图表时,如果仅使用 window.onresize 监听窗口大小变化来触发图表重绘,会导致只有最后一个注册的图表能够响应。这是因为 window.onresize 是一个事件属性,每次赋值都会覆盖前一个。解决此问题的正确方法是使用 window.addeventlistener 为每个图表实例添加独立的 resize 事件监听器,并确保在组件卸载时清理这些监听器,从而保证所有图表都能正确响应窗口大小变化。

理解多 ECharts 实例的尺寸调整问题

在 React 应用中集成 ECharts 图表时,为了确保图表能够适应不同屏幕尺寸或容器大小的变化,通常需要监听浏览器窗口的 resize 事件,并在事件触发时调用 ECharts 实例的 resize() 方法。然而,当页面中存在多个 ECharts 组件实例时,如果每个组件都尝试通过直接赋值 window.onresize 来注册其重绘逻辑,就会遇到一个常见问题:只有最后一个被渲染的 ECharts 图表能够响应窗口大小变化,而之前的图表则保持不变。

问题分析:window.onresize 的局限性

window.onresize 是一个 DOM 事件属性,它只能绑定一个事件处理函数。这意味着每当一个组件执行 window.onresize = function() { ... } 时,它都会覆盖之前任何组件设置的 onresize 处理函数。因此,当页面中有多个 SimpleLine 组件实例时,它们会依次设置自己的 onresize 处理函数,最终只有最后一个组件的 onresize 会生效。

原始的 SimpleLine 组件中存在问题的代码片段如下:

useEffect(() => {
  window.onresize = function () {
      chartInstance2.resize();
  };
  return () => {
      chartInstance2 && chartInstance2.dispose();
  }; 
}, []);
登录后复制

这段代码在每次组件挂载时都会尝试重新赋值 window.onresize,导致了上述问题。

解决方案:使用 window.addEventListener

为了解决 window.onresize 的覆盖问题,我们应该使用 window.addEventListener 方法。addEventListener 允许为同一个事件类型注册多个事件处理函数,而不会互相覆盖。每个组件实例都可以独立地注册自己的 resize 事件处理函数,确保所有图表都能在窗口大小变化时正确重绘。

修改后的 useEffect 钩子应该如下所示:

useEffect(() => {
  // 定义一个事件处理函数
  const handleResize = () => {
    chartInstance2 && chartInstance2.resize();
  };

  // 添加事件监听器
  window.addEventListener("resize", handleResize);

  // 组件卸载时清理 ECharts 实例并移除事件监听器
  return () => {
    window.removeEventListener("resize", handleResize);
    chartInstance2 && chartInstance2.dispose();
  };
}, [chartInstance2]); // 确保当 chartInstance2 变化时重新注册监听器
登录后复制

在这个改进后的代码中:

遨虾
遨虾

1688推出的跨境电商AI智能体

遨虾 69
查看详情 遨虾
  1. 我们定义了一个名为 handleResize 的函数来封装 chartInstance2.resize() 调用。
  2. window.addEventListener("resize", handleResize) 为当前组件实例添加了一个 resize 事件监听器。
  3. 在 useEffect 的清理函数中,我们通过 window.removeEventListener("resize", handleResize) 移除了对应的事件监听器。这一步至关重要,它可以防止内存泄漏,并确保当组件卸载时,其事件处理函数不再被调用。
  4. 将 chartInstance2 添加到 useEffect 的依赖数组中,以确保当图表实例本身发生变化时(尽管在这个例子中不太可能,但这是一个良好的实践),事件监听器能够被正确地重新注册。

完整的 ECharts 组件示例

结合上述修改,一个能够正确处理多个 ECharts 实例尺寸调整的 React 组件 SimpleLine.js 示例如下:

import React, { useRef, useEffect, useState } from "react";
import * as echarts from "echarts";

// 示例数据,实际应用中可能从 props 或 API 获取
let xAxisDatas = [
  "Jan 01", "Jan 02", "Jan 03", "Jan 04", "Jan 05", "Jan 06", "Jan 07", "Jan 08", "Jan 09", "Jan 10",
  "Jan 11", "Jan 12", "Jan 13", "Jan 14", "Jan 15", "Jan 16", "Jan 17", "Jan 18", "Jan 19", "Jan 20",
  "Jan 21", "Jan 22", "Jan 23", "Jan 24", "Jan 25", "Jan 26", "Jan 27", "Jan 28", "Jan 29", "Jan 30", "Jan 31",
  // ... 更多数据
];

let data = [
  {
    name: 'A',
    type: "line",
    smooth: false,
    showSymbol: true,
    symbolSize: 0.1,
    itemStyle: { color: '#0F4C5C' },
    lineStyle: { width: 5 },
    areaStyle: {
      color: 'transparent',
      opacity: 0.5,
    },
    label: {
      show: true,
      position: 'top',
      color: "#0F4C5C",
      fontSize: 15,
      fontWeight: 'Bold',
    },
    data: [
      31, 49, 36, 36, 30, 36, 36, 34, 38, 40, 34, 36, 32, 35, 34, 35, 32, 30, 37, 32, 40, 40, 33, 39,
      31, 37, 34, 35, 38, 37, 33, 32, 38, 33, 35, 37, 37, 39, 33, 39, 45, 50, 51, 38, 39, 34, 32, 39,
      31, 34, 31, 35, 34, 33, 30, 38, 38, 33, 68, 33, 35, 37, 32, 38, 30, 31, 30, 30, 31, 38, 39, 22,
      32, 30, 35, 35, 37, 40, 31, 38, 34, 30, 36, 30, 34, 61, 31, 40, 40, 32, 35, 33, 39, 30, 34, 33,
    ],
    markLine: {
      silent: true,
      lineStyle: {
        color: '#5d6664'
      },
      data: [{ yAxis: 38 }]
    }
  }
];

var option1 = {
  textStyle: {
    color: "#545454",
    fontFamily: "Source Han Sans",
    fontWeight: "lighter",
    fontSize: '15',
  },
  tooltip: {
    trigger: "axis",
    axisPointer: {
      type: "shadow",
      crossStyle: {
        color: "#999"
      }
    }
  },
  legend: {
    data: ['A'],
    left: "50%",
    top: "2%",
    itemWidth: 10,
    itemHeight: 5
  },
  xAxis: {
    type: "category",
    data: xAxisDatas,
    axisLabel: {
      margin: "10"
    },
    name: "xAxisName",
    nameLocation: "center",
    nameGap: 30,
    nameTextStyle: {
      padding: [5, 0, 0, 0]
    },
    axisTick: {
      alignWithLabel: true,
      inside: true
    }
  },
  yAxis: {
    name: "",
    type: "value",
    splitLine: {
      show: false
    }
  },
  dataZoom: [
    {
      show: false,
      realtime: true,
      start: 50,
      end: 100
    },
    {
      type: "inside",
      realtime: true,
      start: 50,
      end: 100
    }
  ],
  grid: {
    left: '2%',
    top: '10%',
    right: '2%',
    bottom: '12%'
  },
  series: data,
};

function SimpleLine() {
  const chartRef = useRef(null);
  // 使用 useState 存储 chartInstance,确保其在组件生命周期内稳定
  const [chartInstance, setChartInstance] = useState(null);

  // 初始化图表实例
  useEffect(() => {
    if (chartRef.current) {
      const renderedChartInstance = echarts.getInstanceByDom(chartRef.current);
      let instance;
      if (renderedChartInstance) {
        instance = renderedChartInstance;
      } else {
        instance = echarts.init(chartRef.current);
      }
      instance.setOption(option1, true);
      setChartInstance(instance); // 将实例保存到 state
    }
    // 组件卸载时清理 ECharts 实例(如果尚未通过 resize 监听器清理)
    return () => {
      // 这里的清理是备用,主要清理在 resize 监听器中进行
      // chartInstance && chartInstance.dispose();
    };
  }, []); // 空依赖数组确保只在组件挂载时执行一次

  // 监听窗口 resize 事件
  useEffect(() => {
    if (!chartInstance) return; // 确保 chartInstance 已经初始化

    const handleResize = () => {
      chartInstance.resize();
    };

    window.addEventListener("resize", handleResize);

    return () => {
      window.removeEventListener("resize", handleResize);
      chartInstance.dispose(); // 在组件卸载时销毁 ECharts 实例
    };
  }, [chartInstance]); // 依赖 chartInstance,确保当它被设置后才注册监听器

  return (
    <div ref={chartRef} style={{ width: "100%", height: "400px" }}></div>
  );
}

export default SimpleLine;
登录后复制

注意事项与最佳实践

  1. 事件监听器的清理: 在 React 的 useEffect 钩子中,如果添加了事件监听器,务必在返回的清理函数中移除它 (removeEventListener)。这可以有效防止内存泄漏,尤其是在组件频繁挂载和卸载的场景中。

  2. chartInstance 的管理: 将 ECharts 实例存储在 React 的 useState 或 useRef 中是推荐的做法。useState 更适用于需要在依赖项中使用的场景,而 useRef 则适用于不需要触发组件重新渲染的引用。在本例中,使用 useState 并将其作为 useEffect 的依赖项,可以确保当图表实例准备好后才注册 resize 监听器。

  3. 防抖 (Debounce) 或节流 (Throttle): 窗口 resize 事件在拖动窗口时会频繁触发。为了优化性能,避免 ECharts 图表过于频繁地重绘,可以考虑对 handleResize 函数进行防抖或节流处理。例如,使用 Lodash 库的 debounce 函数:

    import { debounce } from 'lodash';
    
    // ...
    
    useEffect(() => {
      if (!chartInstance) return;
    
      const handleResize = () => {
        chartInstance.resize();
      };
    
      // 使用 debounce 包装事件处理函数,例如每 100ms 触发一次
      const debouncedHandleResize = debounce(handleResize, 100);
    
      window.addEventListener("resize", debouncedHandleResize);
    
      return () => {
        window.removeEventListener("resize", debouncedHandleResize);
        debouncedHandleResize.cancel(); // 取消任何待处理的防抖调用
        chartInstance.dispose();
      };
    }, [chartInstance]);
    登录后复制
  4. ResizeObserver (高级): 对于更复杂的布局或需要监听特定 DOM 元素尺寸变化的场景,可以考虑使用 ResizeObserver API。它允许您在元素的尺寸发生变化时得到通知,而不仅仅是窗口的尺寸变化。这在图表容器本身尺寸变化(而非窗口尺寸变化)时重绘图表非常有用。

通过采纳 window.addEventListener 和正确的 useEffect 清理机制,可以确保在 React 应用中渲染的多个 ECharts 图表都能够独立且正确地响应窗口大小变化,从而提供更稳定和响应式的用户体验。

以上就是React 中 ECharts 多实例窗口调整大小失效的解决方案的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号