Resium配置terrainProvider后状态更新触发地图重绘闪烁如何解决
问题描述
搭建基于Resium的地图展示示例项目时,给组件绑定onClick、onMouseMove事件,事件回调仅执行独立状态的赋值操作,该状态未在任何位置被引用使用。测试发现只要为Viewer配置terrainProvider属性,状态更新就会触发整个地图重绘、UI闪烁;移除terrainProvider配置后异常消失。
当前使用技术栈:React 17、Resium 1.14.3(内置Cesium 1.86.1),复现代码如下:
import React, { useState } from "react"; import { Cartesian3, Color } from "cesium"; import { Viewer, Entity } from "resium"; import * as Cesium from 'cesium'; export default function App() { const [currentPos, setCurrentPos] = useState(null); const handleMouseClick = (e, t) => { setCurrentPos("xxx"); } return ( <Viewer full onClick={handleMouseClick} onMouseMove={handleMouseClick} // terrainProvider={new Cesium.CesiumTerrainProvider({ url: 'https://api.maptiler.com/tiles/terrain-quantized-mesh-v2/?key=xxxxxxx' })} > <Entity name="Tokyo" position={Cartesian3.fromDegrees(139.767052, 35.681167, 100)} point={{ pixelSize: 10, color: Color.RED }} /> </Viewer> ); }
产生原因
- 内联写在JSX属性上的
new Cesium.CesiumTerrainProvider(...)会在组件每次重渲染时生成一个全新的实例,每次渲染传给terrainProviderprop的引用地址都不相同 - Resium内部对Cesium实例类props做浅比较校验,检测到
terrainProvider引用变化后,会自动销毁旧的地形提供者实例、挂载新实例,触发全量地形瓦片重新请求加载、场景整体重绘,外在表现就是UI闪烁 - 未配置
terrainProvider时不存在实例重建触发逻辑,因此不会出现重绘闪烁问题
解决方案
核心原则是保证Cesium实例的引用稳定性,避免无关重渲染触发实例重建,有两种常用实现方式:
- 不需要响应组件状态的固定配置实例,直接移到组件外部初始化,整个应用生命周期仅创建一次实例
- 配置依赖组件状态的实例,使用
useMemo缓存,仅在配置项真正变化时才重新创建实例
修正后的代码示例:
import React, { useState, useMemo } from "react"; import { Cartesian3, Color } from "cesium"; import { Viewer, Entity } from "resium"; import * as Cesium from 'cesium'; // 固定地形配置直接移到组件外部,仅初始化一次 const terrainProvider = new Cesium.CesiumTerrainProvider({ url: 'https://api.maptiler.com/tiles/terrain-quantized-mesh-v2/?key=xxxxxxx' }); export default function App() { const [currentPos, setCurrentPos] = useState(null); // 如果地形配置依赖组件内变量,用useMemo缓存,依赖数组仅放实际影响地形配置的变量 // const terrainProvider = useMemo(() => new Cesium.CesiumTerrainProvider({ // url: 'https://api.maptiler.com/tiles/terrain-quantized-mesh-v2/?key=xxxxxxx' // }), [/* 地形url、授权key等配置变量 */]); const handleMouseClick = (e, t) => { setCurrentPos("xxx"); } return ( <Viewer full onClick={handleMouseClick} onMouseMove={handleMouseClick} terrainProvider={terrainProvider} > <Entity name="Tokyo" position={Cartesian3.fromDegrees(139.767052, 35.681167, 100)} point={{ pixelSize: 10, color: Color.RED }} /> </Viewer> ); }
额外注意
所有Cesium非原始值类型的实例(包括影像图层、实体、图元、相机配置对象等),都不要直接内联写在JSX属性位置,否则重渲染时的引用变化都会触发Resium的实例重建逻辑,引发性能问题或渲染异常。
内容的提问来源于stack exchange,提问作者Sagi Mann
相关产品推荐
相关产品推荐

