Svelte 官方入门教程(13) - 上下文(Context Api)

svelte cover

上一讲是slots,整体上来说比较简单,今天来学习上下文(Context Api)

一、setContext 及 getContext

组件 Context(上下文) API 为组件提供一种彼此 ‘对话’ 机制,而无需将数据和函数作为属性来传递,或者发送大量的事件,这是一项高级功能,但是很有用。

App

1
2
3
4
5
6
7
8
9
10
11
12
13
<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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<script>
import { onDestroy } from 'svelte';
import { mapbox } from './mapbox.js';

// set the context here...

export let lat;
export let lon;
export let zoom;

let container;
let map;

function load() {
map = new mapbox.Map({
container,
style: 'mapbox://styles/mapbox/streets-v9',
center: [lon, lat],
zoom,
});
}

onDestroy(() => {
if (map) map.remove();
});
</script>

<!-- this special element will be explained in a later section -->
<svelte:head>
<link
rel="stylesheet"
href="https://unpkg.com/mapbox-gl/dist/mapbox-gl.css"
on:load={load}
/>
</svelte:head>

<div bind:this={container}>
{#if map}
<slot />
{/if}
</div>

<style>
div {
width: 100%;
height: 100%;
}
</style>

MapMarker.svelte

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script>
import { mapbox } from './mapbox.js';

// ...get the context here

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 GL 地图的示例应用为例。 我们想显示标记,使用<MapMarker> 组件,但我们不想传递对底层 Mapbox 实例的引用作为每个组件的道具。

上下文 API 有两个部分——setContextgetContext。 如果组件调用 setContext(key, context),那么任何子组件都可以使用 const context = getContext(key) 检索上下文。

让我们先设置上下文。 在 Map.svelte 中,从 svelte 导入 setContext,从 mapbox.js 导入 key,然后调用 setContext

1
2
3
4
5
6
import { onDestroy, setContext } from 'svelte';
import { mapbox, key } from './mapbox.js';

setContext(key, {
getMap: () => map
});

上下文对象可以是你喜欢的任何数据结构。 与生命周期函数一样,setContextgetContext 必须在组件初始化期间调用。 之后调用它 - 例如在 onMount 内部 - 将引发错误。 在这个例子中,由于在组件挂载之前不会创建 map,因此我们的上下文对象包含一个 getMap 函数而不是 map 本身。

在等式的另一边,在 MapMarker.svelte 中,我们现在可以获得对 Mapbox 实例的引用:

1
2
3
4
5
import { getContext } from 'svelte';
import { mapbox, key } from './mapbox.js';

const { getMap } = getContext(key);
const map = getMap();

标记现在可以将自己添加到地图中了。

<MapMarker> 的更完整版本也可以删除和更改属性 ,但我们仅在此处演示上下文。

详细的例子REPL:

二、Context 的 Key

继续使用上面的例子:

mapbox.js 中,你会看到这一行:

1
const key = Symbol();

从技术上讲我们可以使用任何类型的值作为键。
——例如,我们可以使用 setContext('mapbox', ...)。 使用字符串的缺点是不同的组件库可能会不小心使用同一个。 另一方面,使用 symbols 意味着保证键在任何情况下都不会发生冲突,即使您有多个不同的上下文在许多组件层上运行,因为 symbol 本质上是一个唯一标识符。

三、Contexts vs. stores

Contextsstores 看起来很相似。 它们的不同之处在于 store 可用于应用程序的任何部分,而Contexts仅可用于组件及其后代。 如果您想使用一个组件的多个实例,而其中一个的状态不会干扰其他的状态,这会很有帮助。

实际上,您可以将两者一起使用。 由于上下文不是反应式的,随时间变化的值应表示为存储:

1
const { these, are, stores } = getContext(...);

Context 默认不具备反应性,如果需要上下文中的值支持反应性,应将值先存入 store,然后将 store 放到上下文中。

总结

  • Context 本身并不具有响应性。如果你需要在 context 中的值具有响应性,你需要将store传递到context中。
  • 将任意context对象与当前component同指定的key关联。然后,该context通过getContext函数应用到component的子级(包含带slot的内容)。 像生命周期函数一样,必须在component初始化期间调用它。
  • 如果你检索父组件含有的指定最近componentkey,则必须在component初始化期间调用。

Svelte 官方入门教程(13) - 上下文(Context Api)
http://yoursite.com/2022/07/25/svelte-tutorial-13/
作者
昂藏君子
发布于
2022年7月25日
许可协议