前言
笔者在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=L
init()
}, []);
这样之后,地图就加载出来了
需要配置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;
}
}, []);
解决,没问题。