无React Strict Mode时Mapillary无法工作,DOM初始化失败求助
问题:Mapillary Viewer生产环境崩溃,DOM初始化异常
已尝试修复该问题许久,AI工具无法提供有效帮助。
开发环境下一切正常(因Strict Mode导致函数多次执行),但生产环境完全崩溃。无Strict Mode时Mapillary DOM无法完成初始化,Viewer对象没有挂载目标。
我是否对Mapillary的核心机制存在误解?Mapillary DOM是如何初始化的?我怀疑useEffect引发了竞态条件。
添加了[FLOW]日志来追踪应用流程和崩溃节点,但无论开发环境(符合Strict Mode预期)还是生产环境(超出预期),流程表现都很随机,这证明了竞态条件的存在。
以下是GameScreen组件代码:
import React, { useEffect, useState, useRef, useLayoutEffect } from 'react'; import axios from 'axios'; import { Viewer } from 'mapillary-js'; import '../assets/loader.css' console.log("[FLOW] 1. Module loading"); const MAPILLARY_TOKEN = import.meta.env.VITE_MAPILLARY_TOKEN; console.log("[FLOW] 2. MAPILLARY_TOKEN:", !!MAPILLARY_TOKEN); const GameScreen = ({setCurrentCoordinates}) => { console.log("[FLOW] 3. Component function executing"); const viewerRef = useRef(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); // 生成指定范围内的随机坐标 const getRandomCoordinates = () => { const lat = Math.random() * 120 - 60; // 纬度范围:-60 至 60 const lon = Math.random() * 360 - 180; // 经度范围:-180 至 180 return { lat, lon }; }; useLayoutEffect(() => { console.log("[FLOW] 4. Main useEffect running"); const fetchRandomImageAndInitViewer = async () => { console.log("[FLOW] 5. fetchRandomImageAndInitViewer started"); setLoading(true); const maxAttempts = 100; let attempt = 0; let found = false; let imageKey = null; const delta = 5; // 边界框大小 console.log("[FLOW] 6. Starting search loop"); while (!found && attempt < maxAttempts) { attempt++; const { lat, lon } = getRandomCoordinates(); const bbox = `${lon - delta},${lat - delta},${lon + delta},${lat + delta}`; console.log(`[FLOW] 7. 第${attempt}次尝试:使用边界框${bbox}请求`); try { const res = await axios.get( `https://graph.mapillary.com/images`, { params: { access_token: MAPILLARY_TOKEN, fields: 'id,thumb_2048_url,computed_geometry', limit: 1, bbox, is_pano: true }, } ); console.log(`[FLOW] 8. 第${attempt}次尝试获取到API响应`); if (res.data.data && res.data.data.length > 0) { const imageData = res.data.data[0]; imageKey = imageData.id; found = true; console.log("[FLOW] 9. 找到图片:", imageKey, imageData.computed_geometry.coordinates); setCurrentCoordinates({ lat: imageData.computed_geometry.coordinates[1], lon: imageData.computed_geometry.coordinates[0], }); } else { console.log(`[FLOW] 9a. 边界框${bbox}内无图片`); } } catch (err) { console.log(`[FLOW] 9b. 请求图片出错:`, err.message); } } console.log("[FLOW] 10. 搜索循环结束,是否找到图片:", found); if (!found) { console.log('[FLOW] 11a. 达到最大尝试次数,未找到可用图片'); } else { console.log('[FLOW] 11b. 使用图片Key初始化Viewer:', imageKey); if (!viewerRef.current) { console.log("[FLOW] 12. 创建新的Viewer实例"); try { const mlyContainer = document.getElementById('mly'); console.log("[FLOW] 12a. mly容器元素是否存在:", !!mlyContainer); // 延迟确保DOM渲染完成 setTimeout(() => { console.log("[FLOW] 12b. 延迟后创建Viewer"); viewerRef.current = new Viewer({ accessToken: MAPILLARY_TOKEN, container: mlyContainer, imageKey: imageKey, component: { cover: false, direction: false, sequence: false, zoom: false, attribution: false, bearing: false, spatial: false, tag: false, popup: false, image: true, navigation: false, cache: true, keyboard: false } }); console.log("[FLOW] 13. Viewer实例创建成功:", !!viewerRef.current); // 添加事件监听 viewerRef.current.on('load', () => { console.log("[FLOW] 14a. VIEWER加载事件触发!"); }); viewerRef.current.on('error', (err) => { console.log("[FLOW] 14b. VIEWER错误事件:", err); }); }, 100); // 100ms延迟足够 } catch (err) { console.log("[FLOW] 13a. 创建Viewer出错:", err.message); } } else { console.log("[FLOW] 12a. 将现有Viewer移动到图片:", imageKey); try { viewerRef.current.moveTo(imageKey); console.log("[FLOW] 13b. Viewer移动成功"); } catch (err) { console.log("[FLOW] 13c. 移动Viewer出错:", err.message); } } } console.log("[FLOW] 15. 设置loading为false"); setLoading(false); }; fetchRandomImageAndInitViewer(); return () => { console.log("[FLOW] 16. 清理函数执行"); if(viewerRef.current) { try { console.log("[FLOW] 17. 移除Viewer"); viewerRef.current.remove(); viewerRef.current = null; console.log("[FLOW] 18. Viewer移除成功"); } catch (err) { console.log("[FLOW] 18a. 移除Viewer出错:", err.message); } } } }, [setCurrentCoordinates]); useEffect(() => { console.log("[FLOW] 19. CSS检查effect执行"); // 检查CSS是否加载 setTimeout(() => { const cssLoaded = Array.from(document.styleSheets).some(sheet => { try { return sheet.href && sheet.href.includes('mapillary'); } catch (e) { return false; } }); console.log("[FLOW] 20. Mapillary CSS是否加载:", cssLoaded); // 检查mly容器是否存在 const container = document.getElementById('mly'); console.log("[FLOW] 21. mly容器是否存在:", !!container); // 检查是否存在Mapillary DOM元素 const mapillaryElements = document.querySelectorAll('.mapillary-js-dom'); console.log("[FLOW] 22. mapillary-js-dom元素数量:", mapillaryElements.length); }, 1000); }, []); console.log("[FLOW] 23. 渲染组件"); return ( <> <style> {` .mapillary-js-dom { position: absolute; width: 100%; height: 100%; overflow: hidden; } .mapillary-js-canvas { position: absolute; width: 100%; height: 100%; background-color: #000; } `} </style> <div className="game-screen h-screen w-screen flex items-center justify-center bg-gray-100 overflow-hidden"> <div className="street-view-container w-screen h-full bg-black flex items-center justify-center"> <div id='mly' ref={viewerRef.current} className="object-cover h-full w-full" onLoad={() => console.log("[FLOW] 24. mly div加载事件触发")} > {loading && ( <div className="absolute inset-0 flex items-center justify-center bg-black bg-opacity-50"> <div className="loader"></div> </div> )} </div> </div> </div> </> ); }; console.log("[FLOW] 0. 模块解析完成"); export default GameScreen;
内容的提问来源于stack exchange,提问作者Krystian Turek
相关产品推荐
相关产品推荐

