尝试leaflet+webassemly
前言
笔者在github发现rust版本的leaflet,发现是用wasm-bindgen包装的,尝试使用一下
Issues · slowtec/leaflet-rshttps://github.com/slowtec/leaflet-rs
正文
准备
新建一个react项目,安装rsw依赖
pnpm i -D vite-plugin-rsw
cargo install rsw
安装leaflet依赖
pnpm i leaflet
创建一个rsw项目
rsw init
rsw new leaflet-wasm
————————————————————————————————
# rsw.toml
[[crates]]name = "leaflet-wasm"
监听项目
rsw watch
在Cargo.toml文件中配置依赖
[dependencies]
wasm-bindgen = "0.2.100"
leaflet = "0.4.1"
js-sys = "0.3.77"
web-sys = { version = "0.3.77", features = ["console"] }
导入地图
在lib.rs中,导入地图
use leaflet::{LatLng, Map, MapOptions, TileLayer};
use wasm_bindgen::prelude::*;
use web_sys::console;#[wasm_bindgen(start)]
pub fn main() -> Result<(), JsValue> {console::log_1(&"Hello, Leaflet!".into());let options = MapOptions::default();let map = Map::new("map", &options);map.set_view(&LatLng::new(30.66, 104.0), 5.0);add_tile_layer(&map);Ok(())
}fn add_tile_layer(map: &Map) {TileLayer::new("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png").add_to(map);
}
#[wasm_bindgen(start)]
执行初始化会自动执行这个属性宏所标记的函数,即自动执行main函数
上面代码的功能就是展示地图,需要一个id为map的dom对象
前端导入
需要导入wasm生成的js文件,初始化init,即
import init from '../../leaflet-wasm/pkg/leaflet_wasm'
因此代码如下
import style from './MaP.module.css'
import init from '../../leaflet-wasm/pkg/leaflet_wasm'
import {useEffect} from "react";export default function Map() {useEffect(() => {init()}, []);return (<div className={style.map} id="map"></div>)
}
对于css,如下
.map{position: absolute;top: 0;left: 0;height: 100%;width: 100%;
}
解决报错
第一个报错
wasm-bindgen: imported JS function that was not marked as `catch` threw an error: L is not defined
Stack:
ReferenceError: L is not defined....
L没有定义,在leafet中,一般导入leaflet的方法,如下
import L from 'leaflet';
// 或者 import * as L from 'leaflet'
L没有定义,笔者发现有两种方法可以解决
第一种方法
L没有定义,把L导入,并定义在window上,即
import L from 'leaflet'; useEffect(() => {window.L=Linit()}, []);
这样之后,地图就加载出来了
需要配置leaflet全局的css文件
import 'leaflet/dist/leaflet.css';
放到maib.tsx或者其他地方,这个 leaflet全局的css文件非常重要的
第二种方法
导入L写到生成的pkg/leaflet_wasm.js文件或者写到main.tsx中
即
都行。
第二个报错
installHook.js:1wasm-bindgen: imported JS function that was not marked as `catch` threw an error: Map container is already initialized. Stack: Error: Map container is already initialized.
Map container已经初始化了,这其实React的事情
在React中,在开发中,默认使用是StrictMode
<StrictMode><App /></StrictMode>,
在useEffect会渲染两次。如果在init放在useEffect中,就会创建两个地图,就会报错,因此
可以去掉StrictMode,或者使用useRef这个hook
因此,代码如下
let isInit=useRef<boolean>(true);useEffect(() => {if(isInit.current){init()isInit.current = false;}}, []);
解决,没问题。