Интеграция Cleave.js в Svelte строится вокруг идеи внешнего управления DOM-элементом, при котором форматирование пользовательского ввода делегируется библиотеке, а состояние синхронизируется через реактивную модель Svelte. Основная сложность заключается в том, что Cleave.js напрямую работает с DOM-элементами, тогда как Svelte стремится абстрагировать доступ к ним через реактивные привязки и жизненный цикл компонентов.
Cleave.js предоставляет форматирование ввода для различных типов
данных: номера банковских карт, телефоны, даты, числовые значения. В
Svelte это требует аккуратного связывания экземпляра Cleave с
input-элементом и управления его жизненным циклом через
onMount и onDestroy.
Библиотека подключается стандартным способом через пакетный менеджер:
npm install cleave.js
В рамках Svelte-проекта важно учитывать, что Cleave.js работает только в браузерной среде, поэтому любые обращения к нему должны выполняться после монтирования компонента.
Основной механизм подключения заключается в создании экземпляра
Cleave внутри onMount:
<script>
import { onMount, onDestroy } from "svelte";
import Cleave from "cleave.js";
let input;
let cleave;
onMount(() => {
cleave = new Cleave(input, {
creditCard: true,
onValueChanged: (e) => {
// синхронизация значения с состоянием Svelte
}
});
});
onDestroy(() => {
if (cleave) cleave.destroy();
});
</script>
<input bind:this={input} />
Ключевой момент заключается в использовании bind:this,
который предоставляет прямую ссылку на DOM-элемент. Это позволяет
передать элемент в Cleave без обходных решений.
Svelte использует реактивные переменные, поэтому требуется двусторонняя синхронизация: изменения в input должны обновлять состояние, а изменения состояния должны корректно отражаться в Cleave.
<script>
import { onMount } from "svelte";
import Cleave from "cleave.js";
let input;
let value = "";
let cleave;
onMount(() => {
cleave = new Cleave(input, {
numeral: true,
onValueChanged: (e) => {
value = e.target.rawValue;
}
});
});
</script>
<input bind:this={input} bind:value={value} />
Здесь используется rawValue, который предоставляет
неформатированное значение, что особенно важно при работе с числовыми
данными и отправкой на сервер.
В Svelte переменная value может изменяться извне,
например, при загрузке данных или reset-операциях. Cleave.js не
отслеживает изменения автоматически, поэтому требуется ручная
синхронизация:
<script>
import { onMount } from "svelte";
import Cleave from "cleave.js";
let input;
let cleave;
export let value = "";
onMount(() => {
cleave = new Cleave(input, {
date: true,
datePattern: ["d", "m", "Y"]
});
});
$: if (cleave && value !== cleave.getRawValue()) {
cleave.setRawValue(value);
}
</script>
<input bind:this={input} />
Реактивное выражение $: обеспечивает синхронизацию
внешнего состояния с внутренним состоянием Cleave.
Более идиоматичный подход Svelte заключается в использовании actions. Это позволяет полностью отделить логику форматирования от компонента.
// cleaveAction.js
import Cleave from "cleave.js";
export function cleave(node, options) {
const instance = new Cleave(node, options);
return {
upd ate(newOptions) {
instance.destroy();
return new Cleave(node, newOptions);
},
destroy() {
instance.destroy();
}
};
}
Использование:
<script>
import { cleave } from "./cleaveAction";
let value = "";
</script>
<input use:cleave={{ numeral: true }} bind:value />
Action-подход делает интеграцию масштабируемой и повторно используемой.
<input
use:cleave={{
phone: true,
phoneRegionCode: "KZ"
}}
/>
Cleave.js автоматически подстраивает формат под региональные правила, что снижает необходимость ручной обработки строк.
<input
use:cleave={{
date: true,
datePattern: ["Y", "m", "d"]
}}
/>
Особенность интеграции заключается в том, что Svelte не должен хранить форматированное значение — только логическое представление даты.
<input
use:cleave={{
numeral: true,
numeralThousandsGroupStyle: "thousand"
}}
/>
Для финансовых приложений важно использовать rawValue
при отправке данных, чтобы избежать включения разделителей тысяч.
Cleave.js предоставляет событие onValueChanged, которое
является ключевым механизмом интеграции с Svelte.
<script>
import Cleave from "cleave.js";
import { createEventDispatcher, onMount } from "svelte";
const dispatch = createEventDispatcher();
let input;
onMount(() => {
new Cleave(input, {
creditCard: true,
onValueChanged: (e) => {
dispatch("change", {
value: e.target.value,
rawValue: e.target.rawValue
});
}
});
});
</script>
<input bind:this={input} />
Такой подход позволяет интегрировать Cleave.js в более сложные формы с централизованной обработкой состояния.
Svelte может использовать серверный рендеринг, однако Cleave.js
требует доступа к window и DOM. Поэтому любые обращения
должны быть строго ограничены клиентским циклом.
Типичная ошибка — инициализация Cleave вне onMount, что
приводит к сбоям при SSR.
Корректная стратегия:
onMountif (typeof window !== "undefined") {
// безопасная инициализация
}
В некоторых сценариях пересоздание Cleave.js приводит к потере позиции курсора. Поэтому предпочтительно использовать методы обновления:
$: if (cleave) {
cleave.setPhoneRegionCode(region);
}
или
$: if (cleave) {
cleave.setRawValue(value);
}
Подход зависит от типа конфигурации и версии библиотеки.
При работе с большими формами рекомендуется централизовать управление экземплярами Cleave:
Пример стратегии:
const instances = new Map();
export function cleave(node, options) {
const instance = new Cleave(node, options);
instances.se t(node, instance);
return {
destroy() {
instance.destroy();
instances.delete(node);
}
};
}
Такой подход предотвращает утечки памяти при динамическом создании форм.
При использовании сторонних валидаторов важно различать:
Cleave.js должен рассматриваться исключительно как слой
представления, а не источник истины для данных. Валидация всегда должна
опираться на rawValue, иначе возможны ошибки при проверке
чисел, дат и телефонных форматов.