SveltesetContextandgetContext

Svelte setContext and getContext

上下文 API 为组件提供了一种机制,可以在不传递数据和函数作为 props 或分派大量事件的情况下相互“交谈”。这是一项高级功能,但很有用。以使用 Mapbox GL 地图的示例应用程序为例。我们想使用 <MapMarker> 组件显示标记,但我们不想传递对底层 Mapbox 实例的引用作为每个组件的 prop。上下文 API 有两部分 — ​setContext​ 和 ​getContext​。如果组件调用 ​setContext(key, context)​,则任何子组件都可以使用 ​const context = getContext(key)​ 检索上下文。让我们先设置上下文。在 ​Map.svelte​ 中,从 ​svelte​ 导入 ​setContext​,从 ​mapbox.js​ 导入 ​key​ 并调用 ​setContext​:import { onMount, setContext } from 'svelte';

import { mapbox, key } from './mapbox.js';

setContext(key, {

getMap: () => map

});上下文对象可以是您喜欢的任何内容。与生命周期函数一样,​setContext​ 和 ​getContext​ 必须在组件初始化期间调用;由于 ​map​ 在组件挂载之前不会创建,因此我们的 context 对象包含一个 ​getMap​ 函数而不是 ​map​ 本身。在等式的另一边,在 ​MapMarker.svelte​ 中,我们现在可以获得对 Mapbox 实例的引用:import { getContext } from 'svelte';

import { mapbox, key } from './mapbox.js';

const { getMap } = getContext(key);

const map = getMap();标记现在可以将自己添加到地图中。更完善的 ​<MapMarker>​ 版本也可以处理移除和属性更改,但我们在这里只演示上下文。Context keys在 ​mapbox.js​ 中你会看到这一行:const key = {};我们可以使用任何东西作为键——例如我们可以做 ​setContext('mapbox', ...)​ 。使用字符串的缺点是不同的组件库可能会意外地使用同一个字符串;使用对象字面量意味着键在任何情况下都保证不会发生冲突(因为一个对象只对自身具有引用相等性,即 ​{} !== {}​ 而​“x”===“x”​),即使你有跨多个组件层运行的多个不同上下文。Contexts vs. storesContexts 和 stores 看起来很相似。它们的不同之处在于,stores 可用于应用程序的任何部分,而 context 仅可用于组件及其后代。如果你想使用一个组件的多个实例,而一个组件的状态不会干扰其他组件的状态,这会很有帮助。事实上,您可以将两者结合使用。由于 context 不是反应性的,因此随时间变化的值应该表示为存储:const { these, are, stores } = getContext(...);示例代码App.svelte<script>

import Map from './Map.svelte';

import MapMarker from './MapMarker.svelte';

</script>

<Map lat={35} lon={-84} zoom={3.5}>

<MapMarker lat={37.8225} lon={-122.0024} label="Svelte Body Shaping"/>

<MapMarker lat={33.8981} lon={-118.4169} label="Svelte Barbershop & Essentials"/>

<MapMarker lat={29.7230} lon={-95.4189} label="Svelte Waxing Studio"/>

<MapMarker lat={28.3378} lon={-81.3966} label="Svelte 30 Nutritional Consultants"/>

<MapMarker lat={40.6483} lon={-74.0237} label="Svelte Brands LLC"/>

<MapMarker lat={40.6986} lon={-74.4100} label="Svelte Medical Systems"/>

</Map>Map.svelte<script>

import { onMount, setContext } from 'svelte';

import { mapbox, key } from './mapbox.js';

setContext(key, {

getMap: () =&gt; map

});

export let lat;

export let lon;

export let zoom;

let container;

let map;

onMount(() =&gt; {

const link = document.createElement('link');

link.rel = 'stylesheet';

link.href = 'https://unpkg.com/mapbox-gl/dist/mapbox-gl.css';

link.onload = () =&gt; {

map = new mapbox.Map({

container,

style: 'mapbox://styles/mapbox/streets-v9',

center: [lon, lat],

zoom

});

};

document.head.appendChild(link);

return () =&gt; {

map.remove();

link.parentNode.removeChild(link);

};

});

</script>

<style>

div {

width: 100%;

height: 100%;

}

</style>

<div bind:this={container}>

{#if map}

<slot></slot>

{/if}

</div>MapMarker.svelte<script>

import { getContext } from 'svelte';

import { mapbox, key } from './mapbox.js';

const { getMap } = getContext(key);

const map = getMap();

export let lat;

export let lon;

export let label;

const popup = new mapbox.Popup({ offset: 25 })

.setText(label);

const marker = new mapbox.Marker()

.setLngLat([lon, lat])

.setPopup(popup)

.addTo(map);

</script>mapbox.jsimport mapbox from 'mapbox-gl';

// https://docs.mapbox.com/help/glossary/access-token/

mapbox.accessToken = MAPBOX_ACCESS_TOKEN;

const key = {};

export { mapbox, key };