上一讲是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' ; 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 > <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' ; 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 有两个部分——setContext
和 getContext
。 如果组件调用 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 });
上下文对象可以是你喜欢的任何数据结构。 与生命周期函数一样,setContext
和 getContext
必须在组件初始化期间调用。 之后调用它 - 例如在 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
中,你会看到这一行:
从技术上讲我们可以使用任何类型的值作为键。 ——例如,我们可以使用 setContext('mapbox', ...)
。 使用字符串的缺点是不同的组件库可能会不小心使用同一个。 另一方面,使用 symbols
意味着保证键在任何情况下都不会发生冲突,即使您有多个不同的上下文在许多组件层上运行,因为 symbol
本质上是一个唯一标识符。
三、Contexts vs. stores Contexts
和 stores
看起来很相似。 它们的不同之处在于 store
可用于应用程序的任何部分,而Contexts
仅可用于组件及其后代。 如果您想使用一个组件的多个实例,而其中一个的状态不会干扰其他的状态,这会很有帮助。
实际上,您可以将两者一起使用。 由于上下文不是反应式的,随时间变化的值应表示为存储:
1 const { these, are, stores } = getContext(...);
Context 默认不具备反应性,如果需要上下文中的值支持反应性,应将值先存入 store
,然后将 store
放到上下文中。
总结
Context
本身并不具有响应性。如果你需要在 context
中的值具有响应性,你需要将store
传递到context
中。
将任意context对
象与当前component
同指定的key关联。然后,该context
通过getContext
函数应用到component
的子级(包含带slot的内容)。 像生命周期函数一样,必须在component
初始化期间调用它。
如果你检索父组件含有的指定最近component
的 key
,则必须在component
初始化期间调用。