Next.js部署Vercel调用@react-google-maps/api报客户端异常
Next.js部署Vercel后地图组件页面报错问题
问题现象
Next.js应用在本地development开发环境、本地build构建流程中均运行正常,部署至Vercel平台后,访问引入MapView地图组件的页面时触发客户端报错:
Application error: a client-side exception has occurred (see the browser console for more information).
Vercel平台记录的运行日志截图:
相关代码
MapView组件完整实现
import { GoogleMap, Marker, MarkerClusterer, useJsApiLoader } from '@react-google-maps/api' import { useCallback, useState } from 'react' import SpotCard from './spots/SpotCard' export const MapView = ({ data }) => { const [postCardData, setPostCardData] = useState(null) const defaultProps = { center: { lat: -21.115141, lng: 55.536384, }, containerStyle: { maxWidth: '800px', height: '600px', margin: '0 auto', borderRadius: '10px', opacity: 1, }, zoom: 10, } const getImage = (category) => { let source if (category == 'Bivouac') source = `/static/icons/Bivouac.png` if (category == 'Camping') source = `/static/icons/Camping.png` if (category == 'Gîtes') source = `/static/icons/Gîtes.png` if (category == 'Insolite') source = `/static/icons/Insolite.png` return source } const { isLoaded } = useJsApiLoader({ id: 'google-map-script', googleMapsApiKey: 'XXXXXXXXX', }) const [map, setMap] = useState(null) const onLoad = useCallback(function callback(map) { const bounds = new window.google.maps.LatLngBounds(defaultProps.center) map.fitBounds(bounds) setMap(map) }, []) const onUnmount = useCallback(function callback(map) { setMap(null) }, []) return ( isLoaded ? ( <div> <div className=" my-16"> <GoogleMap mapContainerStyle={defaultProps.containerStyle} center={defaultProps.center} zoom={10} onLoad={onLoad} onUnmount={onUnmount} > <MarkerClusterer minimumClusterSize={4}> {(clusterer) => data.length > 1 ? ( data?.map((marker, i) => ( <Marker key={i} position={{ lat: Number(marker.attributes.lat), lng: Number(marker.attributes.lon), }} icon={getImage(marker.attributes.category.data.attributes.name)} onClick={() => setPostCardData(marker)} clusterer={clusterer} /> )) ) : ( <Marker position={{ lat: Number(data.attributes.lat), lng: Number(data.attributes.lon), }} icon={getImage(data.attributes.category.data.attributes.name)} onClick={() => setPostCardData(data)} /> ) } </MarkerClusterer> {postCardData !== null && ( <div className=" z-10 flex h-full items-center justify-center"> <div className=" max-w-xs"> <div className="flex justify-end"> <button className="absolute top-16 z-20 inline-flex items-center justify-center rounded-full bg-white p-2 text-green-600 opacity-95 hover:bg-gray-100 hover:text-green-800 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-green-500" onClick={() => setPostCardData(null)} > <span className="sr-only">Close menu</span> <svg className="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true" > <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12" /> </svg> </button> </div> <SpotCard post={postCardData} /> </div> </div> )} </GoogleMap> </div> </div> ) : ( <></> ) ) }
当前CSP配置
const ContentSecurityPolicy = ` default-src 'self'; script-src 'self' 'unsafe-eval' 'unsafe-inline' giscus.app maps.googleapis.com; style-src 'self' 'unsafe-inline' fonts.googleapis.com; img-src * blob: data: maps.gstatic.com googleapis.com ggpht.com; media-src 'none'; connect-src * googleapis.com google.com gstatic.com data: blob:; font-src 'self' fonts.gstatic.com; frame-src giscus.app google.com `
当前ESLint配置
module.exports = { root: true, env: { browser: true, amd: true, node: true, es6: true, window: true, }, globals: { window: true, document: true, google: true, }, extends: [ 'eslint:recommended', 'plugin:prettier/recommended', 'next', 'next/core-web-vitals', 'google', ], rules: { 'prettier/prettier': ['error', { endOfLine: 'auto' }], 'react/react-in-jsx-scope': 'off', 'react/prop-types': 0, 'no-unused-vars': 0, 'react/no-unescaped-entities': 0, }, }
初步排查方向
目前暂未定位到根因,初步怀疑问题可能与ContentSecurityPolicy配置有关,也不排除来源于ESLint配置,寻求有同类问题处理经验的开发者协助。
内容的提问来源于stack exchange,提问作者sebastien pj
相关产品推荐
相关产品推荐

