Svelte系列 --- 上下文

1,067 阅读2分钟

组件 Context(上下文) API 为组件提供一种彼此 '对话' 机制,而无需将数据和函数作为属性来传递,或者发送大量的事件。类似于vue的provide和inject

Context 相关的 API 函数有两个,分别是setContextgetContext

如果组件调用了setContext(key, context),那么其任意的子组件 都可以通过const context = getContext(key)来获取这个 context。

setContextgetContext类似生命周期函数,只能在组件初始化期间调用。

父组件 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>