Svelte Module Context
tags: Svelte
category: Front-End
description: Svelte Module Context
created_at: 2023/04/19 01:00:00
前言
Module Context
,雖然有 Context
字樣,不過使用方式差滿多,雖然也是有點共享(Context
)的味道。
只是前面所提到的 Context
是上下的傳遞,而這邊的 Module Context
比較像是平行的傳遞,又或者是把東西 export
出去給其他想用的地方使用。
基本使用
假設我有多個 Counter
,然後希望他們共用同一個狀態,可以這樣做:
Counter.svelte
<script lang="ts" context="module">
import { writable } from 'svelte/store';
let sharedValue = writable(0);
</script>
<div>
<button on:click={() => $sharedValue++}>
Clicked {$sharedValue}.
</button>
</div>
然後像這樣使用他:
<script lang="ts">
import Counter from '../components/Counter.svelte';
</script>
<Counter />
<Counter />
<Counter />
這時你任意去戳,他們三個 Counter
的值都會是同步的。
有一種情境可能是有多個組件,你希望點任一個(active
),然後其他變成(inactive
)
例如我有多張圖片(div
代替),點擊任一個後要放大,其他變回原本大小:
<script lang="ts" context="module">
let activeImage: HTMLElement | null = null;
</script>
<script lang="ts">
export let color = '#39f';
let image: HTMLElement | null = null;
</script>
<div
style="background: {color}; width: 100px; height: 100px; border: 1px solid #333; transition: .5s"
bind:this={image}
on:mousedown={() => {
activeImage?.classList.remove('active');
activeImage = image;
activeImage?.classList.add('active');
}}
/>
<style>
:global(.active) {
width: 150px !important;
height: 150px !important;
}
</style>
這邊使用了 !important
,只是為了 Demo
,一般情況請不要使用這個。除非你很清楚你在幹嘛
然後在 style
的部分使用到一個前面沒提過的東西(:global()
),這個是因為 Svelte
預設情況下 style
都是以組件為單位的,而且他經由 compiler
先去做處理,因為上面我增減 class
是透過原生 javascript
去處理的,所以 Svelte
並不知道我有用到他(active
)。
或是也可以把上面的範例改一下,改為用 Svelte
的方式去做:
<script lang="ts" context="module">
import { writable } from 'svelte/store';
let activeImage = writable<HTMLElement | null>(null);
</script>
<script lang="ts">
export let color = '#39f';
let image: HTMLElement | null = null;
</script>
<div
style="background: {color}; width: 100px; height: 100px; border: 1px solid #333; transition: .5s"
bind:this={image}
on:mousedown={() => ($activeImage = image)}
class:active={$activeImage && $activeImage === image}
/>
<style>
.active {
width: 150px !important;
height: 150px !important;
}
</style>
上面這樣改乾淨許多,而且 Svelte
也能輕易知道我有使用到 .active
這個類別,就不用加上 global
了。
不過需要注意的是,activeImage
必須是 reactive
的,因為這樣其他組件才知道他修改了。
Export
出去給其他地方用
有些時候我可能有組件裡面的東西想要 export
出去給其他地方用,例如前幾篇提到的 Context
,如果我就不想要抽離一個單獨檔案去做,想寫在組件裡面,又想用 Symbol
當作 key
的話,就可以這麼做。
這時 +page.svelte
非常簡單:
<script lang="ts">
import Outer from '../components/Outer.svelte';
</script>
<Outer />
Outer.svelte
<script context="module">
export const key = Symbol();
</script>
<script>
import { setContext } from 'svelte';
import Inner from './Inner.svelte';
setContext(key, 'Outer');
</script>
<div>Outer</div>
<Inner />
Inner.svelte
<script lang="ts">
import { getContext } from 'svelte';
import { key } from './Outer.svelte';
const value = getContext(key);
</script>
<div>Inner value: {value}</div>
這樣就可以做更多活用了。
總結
到這邊單純的 Svelte
大概就結束了,完結灑花
Q
: 但不是還有 Router
沒寫嗎?
A
: 那個會在之後 SvelteKit
去寫,雖然其實這個系列一開始就是建立 SvelteKit
專案,只是只用 Svelte
的功能
不過下一篇其實就會是 Router
了,為了做最後的總練習(要跟 Vue 3
對應的話)。