Gatsby集成Maplibre GL运行时报maplibregl is undefined错误如何解决
Gatsby站点引入Maplibre地图运行时报maplibregl未定义错误的解决办法
我想要在Gatsby站点中展示Maplibre地图,该功能在React项目中可以正常实现。
执行gatsby develop命令没有出现报错,但火狐浏览器运行时抛出如下错误:maplibre_gl__WEBPACK_IMPORTED_MODULE_2__.maplibregl is undefined
Chrome浏览器报错如下:
package.json依赖配置
"dependencies": { "gatsby": "^3.13.0", "gatsby-image": "^3.11.0", "gatsby-plugin-feed": "^3.13.0", "gatsby-plugin-layout": "^2.13.0", "gatsby-plugin-local-search": "^2.0.1", "gatsby-plugin-react-helmet": "^4.13.0", "gatsby-plugin-sharp": "^3.13.0", "gatsby-remark-autolink-headers": "^4.10.0", "gatsby-remark-images": "^5.10.0", "gatsby-remark-prismjs": "^5.10.0", "gatsby-source-filesystem": "^3.13.0", "gatsby-transformer-remark": "^4.10.0", "gatsby-transformer-sharp": "^3.13.0", "maplibre-gl": "^1.15.2", "prismjs": "^1.24.1", "prop-types": "^15.7.2", "query-string": "^7.0.1", "react": "^17.0.2", "react-dom": "^17.0.2", "react-helmet": "^6.1.0", "react-use-flexsearch": "^0.1.1" }, "devDependencies": { "eslint": "^7.32.0", "eslint-config-react-app": "^6.0.0", "gatsby-plugin-manifest": "^3.13.0", "postcss": "^8.3.6" }
地图组件map.js代码
import React, { useEffect, useRef } from "react" import PropTypes from "prop-types" import { maplibregl } from "maplibre-gl" import "maplibre-gl/dist/maplibre-gl.css" import { siteMetadata } from "../../gatsby-config" const Map = ({ zoom, center, minZoom, maxZoom }) => { const { mapboxToken, maptilerToken } = siteMetadata // 该ref存储地图DOM节点,用于传入MapLibre GL const mapNode = useRef(null) // 该ref存储实例化后的地图对象,可在其他hook中调用 const mapRef = useRef(null) useEffect(() => { let mapCenter = center let mapZoom = zoom const map = new maplibregl.Map({ container: mapNode.current, style: `https://api.maptiler.com/maps/streets/style.json?key=YymZPIGfniu7apIvln6X`, center: mapCenter, zoom: mapZoom, minZoom, maxZoom, }) mapRef.current = map // window.map = map // 用于控制台调试查询 map.on("load", () => {}) return () => { map.remove() } }, []) return <div ref={mapNode} style={{ width: "100vh", height: "100vh" }} /> } Map.propTypes = { center: PropTypes.arrayOf(PropTypes.number), zoom: PropTypes.number, } Map.defaultProps = { center: [0, 0], zoom: 0, minZoom: 0, } export default Map
页面引入组件的代码
import * as React from "react" import Map from "../components/map" const IndexPage = () => ( <> <h1>Hi people</h1> <p> Welcome to your new Gatsby Maplibre site. Here is a map without extras </p> <Map /> </> ) export default IndexPage
执行gatsby build命令也没有报错,但浏览器开发者工具中可以看到如下错误栈:
TypeError: o.maplibregl is undefined a map.js:24 Fi React unstable_runWithPriority scheduler.production.min.js:18 React 3 D scheduler.production.min.js:16 onmessage scheduler.production.min.js:12 react-dom.production.min.js:216:199 React 5 unstable_runWithPriority scheduler.production.min.js:18 React 4 D scheduler.production.min.js:16 onmessage scheduler.production.min.js:12
运行环境信息
- OS: Linux 5.11 Ubuntu 20.04.3 LTS (Focal Fossa)
- CPU: (2) x64 Intel(R) Pentium(R) CPU 4417U @ 2.30GHz
- Shell: 5.0.17 - /bin/bash
- Node: 14.16.0 - ~/.nvm/versions/node/v14.16.0/bin/node
- Yarn: 1.22.5 - /usr/bin/yarn
- npm: 7.6.2 - ~/.nvm/versions/node/v14.16.0/bin/npm
- Python: 2.7.18 - /usr/bin/python
- Chrome: 92.0.4515.159
- Firefox: 91.0
- gatsby: ^3.13.0 => 3.13.0
- gatsby-cli: 3.13.0
错误原因
这个问题是两个因素共同导致的:
- Maplibre GL 1.x版本默认采用CommonJS导出规范,没有ES模块的命名导出,所以
import { maplibregl } from 'maplibre-gl'这种写法在Webpack打包时无法正确识别模块导出,服务端打包(Gatsby的build阶段是Node环境运行)不会报错,但浏览器端运行时找不到对应的导出对象。 - Gatsby默认会在服务端预渲染所有页面组件,Maplibre依赖浏览器的window、canvas等API,服务端运行时也可能引发隐性问题。
解决步骤
第一步:修改maplibre-gl的引入方式
将map.js中引入maplibre的代码替换为默认导入:
// 替换原来的import { maplibregl } from "maplibre-gl" import maplibregl from 'maplibre-gl'
如果上面的写法仍报错,可以改成导入整个模块再取挂载的对象:
import * as maplibre from 'maplibre-gl' const maplibregl = maplibre.default ?? maplibre.maplibregl
第二步:配置组件仅在客户端加载
Gatsby支持通过loadable-components让地图组件只在浏览器端渲染,避免服务端预渲染的问题:
首先安装依赖:
npm install @loadable/component # 或者 yarn add @loadable/component
然后修改页面引入组件的代码:
import * as React from "react" import loadable from '@loadable/component' // 配置loadable仅在客户端加载地图组件 const Map = loadable(() => import("../components/map"), { ssr: false, // 可选:配置加载中的占位符 fallback: <div>地图加载中...</div> }) const IndexPage = () => ( <> <h1>Hi people</h1> <p> Welcome to your new Gatsby Maplibre site. Here is a map without extras </p> <Map /> </> ) export default IndexPage
修改完成后重新执行gatsby develop或者gatsby build && gatsby serve即可正常加载地图。
内容的提问来源于stack exchange,提问作者astridx
相关产品推荐
相关产品推荐

