leaflet - 苗条的传单
问题描述
我正在尝试结合传单找到进入 Svelte 的方法。我卡住的地方是如何正确地将传单组件拆分为文件。为了学习,我正在尝试使用 svelte构建官方官方传单快速入门。
这就是我的 app.svelte 的样子:
<script>
import L from 'leaflet';
import { onMount } from "svelte";
import { Circle } from "./components/Circle.svelte";
let map;
onMount(async () => {
map = L.map("map");
L.tileLayer("https://a.tile.openstreetmap.org/{z}/{x}/{y}.png ", {
attribution:
'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>',
maxZoom: 18,
tileSize: 512,
zoomOffset: -1
}).addTo(map);
map.setView([51.505, -0.09], 13);
Circle.addTo(map);
});
</script>
<style>
html,body {
padding: 0;
margin: 0;
}
html, body, #map {
height: 100%;
width: 100vw;
}
</style>
<svelte:head>
<link
rel="stylesheet"
href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css"
integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ=="
crossorigin="" />
</svelte:head>
<div id="map" />
和我的圈子组件:
<script context="module">
import L from 'leaflet';
export let map_obj;
export let Circle = L.circle([51.508, -0.11], {
color: "red",
fillColor: '#f03',
fillOpacity: 0.5,
radius: 500
});
</script>
虽然这是有效的,但我认为考虑每个组件并将其添加到地图中并不有效Circle.addTo(map);
。如何将地图对象传递给圆形组件,或者是否有更好的模式来构建包含多个组件的地图?
注意:我知道苗条/传单,但喜欢从头开始学习。
解决方案
由于像 Svelte 这样的框架的生命周期不是很简单,而 Leaflet 的 let-me-do-DOM-stuff 架构非常简单,因此这个看似简单的任务变得很复杂。
有几种方法可以解决这个问题。我将描述一个,基于将 Leaflet 层的 Svelte 组件嵌套在 Leaflet 映射的 Svelte 组件中,并使用setContext
和getContext
处理 LeafletL.Map
实例。(我从https://github.com/beyonk-adventures/svelte-mapbox借用了这种技术)
所以一个 Svelte 组件L.Marker
看起来像:
<script>
import L from 'leaflet';
import { getContext } from "svelte";
export let lat = 0;
export let lng = 0;
let map = getContext('leafletMapInstance');
L.marker([lat, lng]).addTo(map);
</script>
很简单——通过L.Map
从 Svelte 上下文中获取实例getContext
,实例化L.Marker
,添加它。这意味着必须有一个用于设置上下文的地图的 Svelte 组件,这将需要插入的标记组件,即
<script>
import LeafletMap from './LeafletMap.svelte'
import LeafletMarker from './LeafletMarker.svelte'
</script>
<LeafletMap>
<LeafletMarker lat=40 lng=-3></LeafletMarker>
<LeafletMarker lat=60 lng=10></LeafletMarker>
</LeafletMap>
...然后 Leaflet 地图的 Svelte 组件将创建L.Map
实例,将其设置为上下文,然后就完成了,对吧?没那么快。这就是事情变得奇怪的地方。
由于 Svelte 生命周期的工作原理,子组件将在父组件之前“渲染”,但父组件需要一个 DOM 元素来创建L.Map
实例(即地图容器)。所以这可能会延迟到onRender
Svelte 生命周期回调,但这会发生在插槽子被实例化并调用它们的onRender
生命周期回调之后。因此,等待 Svelte 实例化一个包含地图的 DOM 元素,然后实例化L.Map
,然后将该实例传递给上下文,然后在标记元素中获取上下文可能是一场噩梦。
所以相反,一种方法是创建一个分离的 DOM 元素,在L.Map
那里实例化一个,即......
let map = L.map(L.DomUtil.create('div')
...在上下文中设置它,即...
import { setContext } from "svelte";
setContext('leafletMapInstance', map);
...这将允许将由开槽组件实例化的 Leaflet 层添加到分离(因此不可见)的地图中。一旦所有生命周期的东西都让 Svelte 组件L.Map
有一个实际的 DOM 元素附加到 DOM,将地图容器附加到它,即在 Svelte 组件的 HTML 部分中有这个......
<div class='map' bind:this={mapContainer}>
...一旦它实际附加到 DOM,将地图容器附加到它并设置它的大小,即...
let mapContainer;
onMount(function() {
mapContainer.appendChild(map.getContainer());
map.getContainer().style.width = '100%';
map.getContainer().style.height = '100%';
map.invalidateSize();
});
所以这个 Leaflet 的整个 Svelte 组件L.Map
看起来或多或少像......
<script>
import L from "leaflet";
import { setContext, onMount } from "svelte";
let mapContainer;
let map = L.map(L.DomUtil.create("div"), {
center: [0, 0],
zoom: 0,
});
setContext("leafletMapInstance", map);
console.log("map", map);
L.tileLayer("https://a.tile.openstreetmap.org/{z}/{x}/{y}.png ", {
attribution:
'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>',
}).addTo(map);
onMount(() => {
mapContainer.appendChild(map.getContainer());
map.getContainer().style.width = "100%";
map.getContainer().style.height = "100%";
map.invalidateSize();
});
</script>
<svelte:head>
<link
rel="stylesheet"
href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css"
integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ=="
crossorigin=""
/>
</svelte:head>
<style>
.map {
height: 100vh;
width: 100vw;
}
</style>
<div class="map" bind:this="{mapContainer}">
<slot></slot>
</div>
在此处查看一个工作示例。
作为旁注,我会说在将 Leaflet 夹在另一个 JS 框架中之前应该三思而后行,并三思而后行的架构(插槽组件似乎是最干净和最可扩展的,但可能是大数据结构和一些必要的Leaflet 位的编程会更简单)。有时,要理解同时工作的多个框架的生命周期影响可能会非常令人困惑,并且在出现错误时非常耗时。
推荐阅读
- javascript - 运行代码时如何让鼠标处于“等待”模式
- html - 为什么在 React 组件中使用 Tailwind 样式时不应用?
- unity3d - 将 AWS SDK 更新为 .Net SDK 后,我可以知道更改的类吗?
- amazon-web-services - CDK中1个月后如何删除ES日志?
- javascript - 设置 minDate 以避免在 html 输入中选择开始日期之前的结束日期
- javascript - 使用 javascript 执行器想要使用 xpath 点击 weburl 选项卡?
- hive - 不知道hive日志目录?
- javascript - 关于 php 和 node.js 的问题及答案
- javascript - 为什么我的 href 在 Django Python 的 url 中生成 #?
- reactjs - 当我单击 React 中的提交按钮时如何重置表单