常见报错排雷指南2:导出失败的官方解法

二、导出失败:导出模块与导出服务器的排雷

2.1 痛点场景

  • 右上角"⋯"菜单点了没反应,或菜单里只有"打印"没有下载项。
  • 点"下载 PNG/JPG"后图片空白、控制台报 SecurityError / CORS / Failed to execute 'toDataURL'
  • 用 React / 打包工具后,本地能跑,一导出就报错 exporting is not defined

2.2 原理解析

导出功能不在 Highcharts Core 里,而是两个独立模块:highcharts/modules/exporting(生成菜单与图片)和 highcharts/modules/export-data(导出 CSV/XLS)。没显式 import 并初始化它们,菜单就不会出现。

默认导出走官方导出服务器 export.highcharts.com;当图表里用了跨域位图(如 plotBackgroundImage、图片型 dataLabels、图片填充)且图片服务器没有返回 CORS 头时,canvas 被"污染",浏览器禁止 toDataURL,于是报 SecurityError

2.3 官方解法与代码

按当前 @highcharts/react v5 的方式调整。最重要的区别是:在 React 中不要再手动调用旧式模块初始化函数,优先使用 @highcharts/react/modules/* 组件。

1. 原生 JavaScript:加载导出模块

在原生 JavaScript 或传统打包方式中,需要加载并初始化 exporting。如果还要导出 CSV/XLS,则同时加载 export-data

import Highcharts from 'highcharts';
import Exporting from 'highcharts/modules/exporting';
import ExportData from 'highcharts/modules/export-data';

Exporting(Highcharts);
ExportData(Highcharts);

Highcharts.chart('container', {
  title: {
    text: '导出示例'
  },
  exporting: {
    enabled: true
  },
  series: [{
    name: '示例数据',
    data: [29, 71, 106, 129]
  }]
});

不同构建环境对模块导出的形式可能不同。如果使用的是当前 Highcharts ESM 构建,优先遵循对应版本的模块加载方式;不要在同一个项目中混用多个 Highcharts 实例,否则模块可能注册到了错误的实例上。

2. @highcharts/react v5:使用模块组件

当前推荐使用 @highcharts/react@5 的模块组件:

import React from 'react';
import { createRoot } from 'react-dom/client';
import { Chart, Title } from '@highcharts/react';
import { Accessibility } from '@highcharts/react/modules/Accessibility';
import { Exporting } from '@highcharts/react/modules/Exporting';
import { Data } from '@highcharts/react/modules/Data';
import { LineSeries } from '@highcharts/react/series/Line';

function App() {
  return (
    <Chart
      options={{
        exporting: {
          enabled: true,
          filename: 'my-chart',
          buttons: {
            contextButton: {
              menuItems: [
                'downloadPNG',
                'downloadJPEG',
                'downloadPDF',
                'downloadSVG',
                'separator',
                'downloadCSV',
                'downloadXLS'
              ]
            }
          }
        }
      }}
    >
      <Title>导出示例</Title>
      <Accessibility />
      <Exporting />
      <Data />
      <LineSeries
        name="示例数据"
        data={[29, 71, 106, 129]}
      />
    </Chart>
  );
}

createRoot(document.getElementById('container')!).render(<App />);

说明:

  • Exporting 提供导出菜单、打印、PNG/JPEG/PDF/SVG 等功能。
  • Data 提供 CSV/XLS 等数据导出功能。
  • Accessibility 建议一并启用。
  • 当前 v5 的模块路径是 @highcharts/react/modules/Exporting,不是旧的 @highcharts/react/options/Exporting
  • @highcharts/react 示例建议使用 Highcharts 12.x 兼容版本。

如果只需要图片导出,不需要 CSV/XLS,可以省略 Data

3. 菜单不显示或点击无反应

按以下顺序排查:

  1. 是否加载了 Exporting 模块。
  2. 是否配置了 exporting.enabled: true
  3. 是否被全局配置或响应式规则覆盖为 false
  4. 是否误用了 exporting.buttons.contextButton.menuItems,并把所需菜单项删掉了。
  5. 是否项目中存在多个 Highcharts 实例,导致模块加载到另一实例。
  6. 如果是 React/Next.js,是否在客户端组件中渲染图表。

例如,下面的配置会只保留指定菜单项:

exporting: {
  enabled: true,
  buttons: {
    contextButton: {
      menuItems: [
        'downloadPNG',
        'downloadSVG',
        'downloadCSV'
      ]
    }
  }
}

如果需要完整默认菜单,也可以不覆盖 menuItems,只设置:

exporting: {
  enabled: true
}

4. 导出空白、SecurityError 或 CORS 错误

这通常不是导出菜单问题,而是图表包含跨域图片资源,例如:

  • chart.plotBackgroundImage
  • 图片填充
  • 图片型数据标签
  • 自定义 SVG 中引用的外部图片
  • 图片纹理或点图形

浏览器端生成图片时,如果 canvas 使用了没有正确 CORS 响应头的图片,就可能被污染,随后 toDataURL() 会抛出 SecurityError

处理顺序建议如下:

方案 A:为图片资源配置 CORS

图片服务器应返回类似:

Access-Control-Allow-Origin: https://your-site.example

或在确实允许所有来源时返回:

Access-Control-Allow-Origin: *

同时图片请求必须以匿名 CORS 方式加载。仅在 Highcharts 配置中添加一个 CORS 字段,不能替代图片服务器的响应头。

方案 B:避免导出链路中的位图

如果图表允许,改用:

  • 纯色填充
  • SVG 图形
  • 不依赖外部图片的自定义标记

这是最稳定的客户端导出方案。

方案 C:使用服务端导出

默认导出通常可使用 Highcharts 导出服务器。也可以显式设置:

exporting: {
  serverURL: 'https://export.highcharts.com'
}

但需要注意:

  • 图表 SVG 中的外部资源必须能被导出服务器访问。
  • 内网地址、需要登录的地址、受防火墙保护的资源,导出服务器通常无法访问。
  • 将图表或数据发送到第三方服务器可能涉及隐私和合规问题。对敏感数据,建议使用自有导出服务或客户端导出。

5. 内网或离线环境:客户端导出

如果不希望请求 Highcharts 导出服务器,可以使用 offline-exporting

import Highcharts from 'highcharts';
import Exporting from 'highcharts/modules/exporting';
import OfflineExporting from 'highcharts/modules/offline-exporting';

Exporting(Highcharts);
OfflineExporting(Highcharts);

Highcharts.chart('container', {
  exporting: {
    enabled: true,
    fallbackToExportServer: false,
    libURL: '/highcharts/lib/'
  },
  series: [{
    data: [1, 3, 2, 4]
  }]
});

libURL 应指向本地依赖文件所在目录,而不是在真正离线环境中继续使用公网 URL。具体依赖和目录内容应根据所使用的 Highcharts 版本和构建方式部署。

对于 React v5,如果项目版本提供对应的 OfflineExporting 模块组件,优先使用:

import { OfflineExporting } from '@highcharts/react/modules/OfflineExporting';

如果当前包版本没有该组件,则通过 Highcharts 的 ESM 模块路径加载:

import 'highcharts/es-modules/masters/modules/offline-exporting.src.js';

不要把旧版 highcharts/modules/* 初始化写法和 React v5 的模块组件方式混在同一个实例上。

6. Next.js 注意事项

Next.js App Router 中,包含图表和导出模块的组件应为客户端组件:

'use client';

此外,Highcharts 依赖浏览器 DOM,不能在服务端渲染阶段创建图表。若仍出现 window is not defineddocument is not defined 或模块初始化错误,应检查:

  • 图表组件是否标记为客户端组件。
  • 是否在服务端模块顶层执行了依赖 DOM 的逻辑。
  • 是否使用了同一份 Highcharts 实例。
  • 是否将 @highcharts/react 与旧的 highcharts-react-official 混用。

官方参考:

我也要推广
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值