Интеграция сторонних DOM-ориентированных библиотек в Svelte требует
понимания жизненного цикла компонентов и принципа реактивности. Tom
Sel ect работает поверх нативного <select> или
<input> и самостоятельно управляет DOM, что делает
его несовместимым с прямым декларативным подходом Svelte без
адаптационного слоя.
Основная задача интеграции заключается в синхронизации трёх уровней состояния:
Ключевой принцип — единый источник истины в Svelte, а библиотека выступает как UI-слой.
Структура компонента обычно строится вокруг onMount,
onDestroy и ссылок на DOM-элемент.
<script>
import { onMount, onDestroy } fr om "svelte";
import TomSelect from "tom-select";
export let options = [];
export let value = null;
export let placeholder = "Sel ect...";
let selectEl;
let instance;
onMount(() => {
instance = new TomSelect(selectEl, {
options,
placeholder,
valueField: "value",
labelField: "label",
searchField: "label"
});
if (value) {
instance.setValue(value);
}
instance.on("change", (val) => {
value = val;
});
});
onDestroy(() => {
if (instance) instance.destroy();
});
</script>
<sel ect bind:this={selectEl}></select>
Svelte обновляет переменные реактивно, но Tom Sel ect не знает о
внешних изменениях состояния. Поэтому требуется явная синхронизация
через afterUpdate.
<script>
import { onMount, onDestroy, afterUpdate } fr om "svelte";
import TomSelect fr om "tom-select";
export let value;
let selectEl;
let instance;
onMount(() => {
instance = new TomSelect(selectEl, {});
instance.on("change", (val) => {
value = val;
});
});
afterUpdate(() => {
if (instance && instance.getValue() !== value) {
instance.setValue(value);
}
});
onDestroy(() => instance?.destroy());
</script>
<sel ect bind:this={selectEl}></select>
Основная сложность при работе с Tom Sel ect внутри Svelte — это циклические обновления:
value → обновление Tom SelectvalueРешение заключается в флаговой синхронизации:
<script>
import { onMount, onDestroy, afterUpdate } fr om "svelte";
import TomSelect fr om "tom-select";
export let value;
let selectEl;
let instance;
let internalUpdate = false;
onMount(() => {
instance = new TomSelect(selectEl);
instance.on("change", (val) => {
internalUpdate = true;
value = val;
internalUpdate = false;
});
});
afterUpdate(() => {
if (!instance || internalUpdate) return;
if (instance.getValue() !== value) {
instance.setValue(value);
}
});
onDestroy(() => instance?.destroy());
</script>
<sel ect bind:this={selectEl}></select>
Tom Sel ect поддерживает динамическое изменение списка опций, однако в Svelte это требует явного пересоздания или обновления данных через API экземпляра.
<script>
import { onMount, afterUpdate } fr om "svelte";
import TomSelect fr om "tom-select";
export let options = [];
let selectEl;
let instance;
onMount(() => {
instance = new TomSelect(selectEl, {
options
});
});
afterUpdate(() => {
if (!instance) return;
instance.clearOptions();
instance.addOptions(options);
instance.refreshOptions(false);
});
</script>
<sel ect bind:this={selectEl}></select>
В сценариях с удалёнными API важно учитывать задержки и предотвращать гонки состояний.
<script>
import { onMount } fr om "svelte";
import TomSelect fr om "tom-select";
let selectEl;
let instance;
async function loadOptions(query) {
const res = await fetch(`/api/search?q=${query}`);
const data = await res.json();
instance.clearOptions();
instance.addOptions(data);
instance.refreshOptions(false);
}
onMount(() => {
instance = new TomSelect(selectEl, {
load: (query, callback) => {
loadOptions(query).then(() => callback());
}
});
});
</script>
<sel ect bind:this={selectEl}></select>
Компонент-обёртка позволяет стандартизировать использование Tom Sel ect во всём приложении.
<script>
import { onMount, onDestroy, createEventDispatcher } fr om "svelte";
import TomSelect fr om "tom-select";
export let options = [];
export let value = null;
export let settings = {};
const dispatch = createEventDispatcher();
let selectEl;
let instance;
onMount(() => {
instance = new TomSelect(selectEl, {
...settings,
options
});
instance.setValue(value);
instance.on("change", (val) => {
value = val;
dispatch("change", val);
});
});
onDestroy(() => instance?.destroy());
</script>
<sel ect bind:this={selectEl}></select>
Tom Sel ect изначально поддерживает multi-select режим, но в Svelte важно правильно синхронизировать массив значений.
<script>
import { onMount } fr om "svelte";
import TomSelect fr om "tom-select";
export let value = [];
let selectEl;
let instance;
onMount(() => {
instance = new TomSelect(selectEl, {
plugins: ["remove_button"],
maxItems: null
});
instance.on("change", (val) => {
value = val;
});
instance.setValue(value);
});
</script>
<sel ect multiple bind:this={selectEl}></select>
При масштабных приложениях интеграция с store повышает предсказуемость состояния.
// store.js
import { writable } fr om "svelte/store";
export const selectedValues = writable([]);
<script>
import { onMount } fr om "svelte";
import TomSelect from "tom-select";
import { selectedValues } from "./store";
let selectEl;
let instance;
onMount(() => {
instance = new TomSelect(selectEl);
instance.on("change", (val) => {
selectedValues.set(val);
});
selectedValues.subscribe((val) => {
instance.setValue(val);
});
});
</script>
<sel ect bind:this={selectEl}></select>
Корректное уничтожение экземпляра Tom Select критично при частом маунте/анмаунте компонентов.
onDestroy(() => {
if (instance) {
instance.destroy();
instance = null;
}
});
Дополнительно важно учитывать:
При использовании TypeScript повышается надёжность интеграции.
import TomSelect fr om "tom-select";
let instance: TomSelect | null = null;
let selectEl: HTMLSelectElement;
Типизация особенно важна при работе с динамическими опциями и пользовательскими плагинами Tom Sel ect.
Для крупных проектов выделяются устойчивые шаблоны:
Каждый из них снижает связанность между Svelte и Tom Select и упрощает масштабирование интерфейса.
SvelteKit и SSR требуют инициализации только на клиенте:
import { browser } fr om "$app/environment";
onMount(() => {
if (!browser) return;
instance = new TomSelect(selectEl);
});
Tom Select не может быть инициализирован на сервере из-за зависимости от DOM.
При больших списках ключевыми становятся:
refreshOptions(false) вместо полного
rebuildОптимизация взаимодействия с Tom Select особенно важна в формах с высокой динамикой и частыми обновлениями состояния.