组件 Context(上下文) API 为组件提供一种彼此 '对话' 机制,而无需将数据和函数作为属性来传递,或者发送大量的事件。类似于vue的provide和inject
Context 相关的 API 函数有两个,分别是setContext和getContext
如果组件调用了setContext(key, context),那么其任意的子组件 都可以通过const context = getContext(key)来获取这个 context。
setContext及getContext类似生命周期函数,只能在组件初始化期间调用。
父组件 App.svelte
<script>
import { setContext } from 'svelte'
import Child1 from './Child1.svelte'
import Child2 from './Child2.svelte'
// Context 的值可以是任意数据类型
setContext('child1', {
msg: 'child1 message'
})
setContext('child2', {
msg: 'child2 message'
})
</script>
<Child1 />
<Child2 />
子组件
<!-- Child1.svelte -->
<h2>{ctx.msg}</h2>
<script>
import { getContext } from 'svelte'
let ctx
// 如果子组件的父组件设置了名为child1的context,子组件可以通过getContext方法获取
// 如果父组件没有设置对应的context或者子组件是单独使用的时候,子组件可能无法获取到有效的context
// 所以在使用的时候,需要进行相应的逻辑判断
if (getContext('child1')) {
ctx = getContext('child1')
}
</script>
<!-------------------------------------->
<!-- Child2.svelte -->
<h2>{ctx.msg}</h2>
<script>
import { getContext } from 'svelte'
let ctx
if (getContext('child2')) {
ctx = getContext('child2')
}
</script>
在设置context的时候,原则上可以使用任何的数据类型作为context的key值,但推荐使用引用数据类型作为context的key
例如:setContext({}, ...),这样可以有效的避免key值冲突的问题(因为{} !== {}而"x" === "x")
<!-- 父组件 App.svelte -->
<script>
import { setContext } from 'svelte'
import Middle from './Middle.svelte'
setContext('child', {
msg: 'App message'
})
</script>
<Middle />
<!-- 中间组件 Middle.svelte -->
<Child />
<script>
import { setContext } from 'svelte'
import Child from './Child.svelte'
setContext('child', {
msg: 'Middle message'
})
</script>
<!-- 子组件 Child.svelte -->
<h2>{ctx.msg}</h2> <!-- 输出内容为 Middle message -->
<script>
import { getContext } from 'svelte'
let ctx
if (getContext('child')) {
ctx = getContext('child')
}
</script>
Contexts vs Props vs Stores
| 类别 | 描述 |
|---|---|
| Contexts | 父传孙或所有后代 (包括中间隔了几代的子组件或使用slot方式传递过来的子组件) 数据不是响应式的 |
| Props | 父传子 数据是响应式的 |
| Stores | 任意组件和组件之间(不需要是父子) 数据是响应式的 |
Context 默认不具备反应性,如果需要上下文中的值支持反应性,
应将值先存入 store,然后将 store 放到上下文中。
模块上下文
一般情况下,script中包含的代码是每次实例化组件时都需要执行的代码
如果我希望有些代码在组件第一次实例化的时候被执行,之后就不在执行,此时就可以使用模块上下文
模块上下文的主要用途,是提供一种方法,让某个组件的所有实例都共享同一个上下文
如果需要在多个组件之间共享的值,推荐还是使用store
模块上下文写在 <script context="module"> 这个顶层 script 标记中,这是 Svelte 唯一允许你使用的第二个 顶层 script
可以从常规的 <script>中访问模块上下文中声明的值,反过来则不然。
注意:定义在 module 脚本中的变量,都不具备响应性的。因此赋值操作不会触发它重新渲染,即使变量本身会更新。
<!-- Child.svelte -->
<script context="module">
// 在script上设置了 context="module"后,这个scipt块中的代码只会在第一次实例化的执行被执行
// 后续再次实例化组件时将不再被执行
const elements = new Set();
// 从context="module"代码块中导出的任意内容,都将成为模块本身的导出
// 当前这里的导出只能是普通导出,无法使用默认导出,因为组件本身使用的就是默认导出
export function stopAll() {
elements.forEach(element => element.pause() });
}
</script>
<!-- App.svelte -->
<script>
import Child, { stopAll } from './Child.svelte';
</script>