Next.js迁移后Chart.js注解缺失、canvas元素渲染异常问题
问题根因
两类canvas渲染异常和SSR、插件注册、客户端初始化时机直接相关:
- Chart.js注解缺失:你仅对
react-chartjs-2的Chart组件做了ssr:false动态导入,但未注册chartjs-plugin-annotation插件。CRA环境下该插件大概率在项目入口文件被全局注册,迁移Next.js后模块作用域隔离,未显式注册的插件不会被Chart.js加载,这一问题和是否关闭SSR无直接关系。 - ArcGIS 3D canvas渲染异常:3D WebGL渲染强依赖浏览器端
window、WebGLRenderingContext全局API,需要DOM节点挂载完成后才能初始化上下文。如果ArcGIS相关模块在服务端渲染阶段被引入执行,会因拿不到浏览器API导致上下文初始化失败,3D元素无法绘制。
修复步骤
1. 修复Chart.js注解不显示问题
首先安装annotation插件依赖(未安装时执行):npm install chartjs-plugin-annotation
修改父组件的Chart注册逻辑,将Annotation插件加入注册列表,同时优化动态导入配置:
// parent component 修正后的导入和注册部分 import dynamic from 'next/dynamic'; import { Chart as ChartJS, LinearScale, CategoryScale, BarElement, PointElement, LineElement, Legend, Tooltip, } from 'chart.js'; // 新增annotation插件导入 import Annotation from 'chartjs-plugin-annotation'; import moment from 'moment-timezone'; import React, { useRef } from 'react'; import { ArrowRight } from '../../icons/ArrowRight'; import Link from 'next/link'; import { chartOptions } from '../../lib/chartOptions'; const Chart = dynamic((): any => import('react-chartjs-2').then((m: any) => m.Chart), { ssr: false, // 增加加载占位,避免组件 hydration 不匹配 loading: () => <div className="h-[100px] w-[99%] bg-gray-100 animate-pulse"></div> }); // 注册插件时加入Annotation ChartJS.register( LinearScale, CategoryScale, BarElement, PointElement, LineElement, Legend, Tooltip, Annotation // 新增该行 ); // 其余组件逻辑保持不变 export const MarkupCard = ({ item }: any) => { // 原有逻辑不变 }
注意:如果使用Next.js 13+ App Router模式,该组件顶部需要新增'use client'指令,标记为客户端组件。
2. 修复ArcGIS 3D canvas渲染异常
所有ArcGIS 3D相关模块禁止在组件顶层导入,必须在useEffect内动态引入,确保仅在客户端DOM挂载完成后执行初始化:
// ArcGIS地图组件修正示例 import React, { useEffect, useRef } from 'react'; import dynamic from 'next/dynamic'; const ArcGISMap = () => { const mapContainer = useRef<HTMLDivElement>(null); useEffect(() => { let viewInstance: any = null; // 所有ArcGIS模块全部在useEffect内动态导入 const initMap = async () => { const [Map, SceneView] = await Promise.all([ import('@arcgis/core/Map'), import('@arcgis/core/views/SceneView') ]); const map = new Map.default({ basemap: 'topo-vector', ground: 'world-elevation' }); viewInstance = new SceneView.default({ container: mapContainer.current, map: map, // 原有相机、图层配置保持不变 }); } initMap(); // 组件卸载时销毁实例,避免内存泄漏 return () => viewInstance?.destroy(); }, []); return <div ref={mapContainer} className="w-full h-[600px]"></div> } // 整个地图组件关闭SSR export default dynamic(() => Promise.resolve(ArcGISMap), { ssr: false });
效果对比
- CRA环境正常效果:

- 修复前Next.js环境异常效果:

内容的提问来源于stack exchange,提问作者AvidDabbler
相关产品推荐
相关产品推荐

