前言
筆者在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;}}, []);
解決,沒問題。