Svelte строит реактивность вокруг присваиваний и жизненного цикла компонентов, что делает интеграцию сторонних DOM-библиотек, таких как AutoNumeric, задачей, требующей аккуратного управления инициализацией и обновлением состояния. AutoNumeric работает напрямую с DOM-элементами input, преобразуя вводимые значения в форматированные числовые представления, поэтому в контексте Svelte ключевым становится контроль синхронизации между реактивной моделью и внутренним состоянием экземпляра AutoNumeric.
Svelte actions предоставляют наиболее естественный способ интеграции библиотек, работающих с DOM. AutoNumeric удобно инкапсулировать в action, который отвечает за инициализацию, обновление и уничтожение экземпляра.
Основная идея заключается в том, что action получает DOM-элемент и параметры конфигурации, после чего создает экземпляр AutoNumeric и связывает его жизненный цикл с компонентом.
import AutoNumeric from "autonumeric";
export function autoNumeric(node, options = {}) {
const instance = new AutoNumeric(node, options);
return {
upd ate(newOptions) {
instance.update(newOptions);
},
destroy() {
instance.remove();
}
};
}
Использование в компоненте Svelte:
<script>
import { autoNumeric } from "./autoNumericAction";
let value = 12345.67;
const options = {
decimalPlaces: 2,
digitGroupSeparator: " ",
decimalCharacter: ","
};
</script>
<input
use:autoNumeric={options}
bind:value
/>
Однако прямой bind:value в таком виде не синхронизирует
состояние корректно, поскольку AutoNumeric перехватывает ввод и
управляет значением через собственный API.
AutoNumeric предоставляет методы получения и установки числового значения без форматирования. В Svelte требуется вручную связать изменения DOM и реактивной переменной.
Расширенный вариант action:
import AutoNumeric from "autonumeric";
export function autoNumeric(node, options) {
const instance = new AutoNumeric(node, options);
function updateFromNode() {
const numericValue = instance.getNumber();
node.dispatchEvent(
new CustomEvent("numeric-change", {
detail: numericValue
})
);
}
node.addEventListener("input", updateFromNode);
return {
upd ate(newOptions) {
instance.update(newOptions);
},
destroy() {
node.removeEventListener("input", updateFromNode);
instance.remove();
}
};
}
Использование с ручной синхронизацией:
<script>
import { autoNumeric } from "./autoNumericAction";
let value = 0;
function handleChange(event) {
value = event.detail;
}
</script>
<input
use:autoNumeric={{ decimalPlaces: 2 }}
on:numeric-change={handleChange}
/>
<p>{value}</p>
Такой подход разделяет форматированное отображение и числовое состояние, что соответствует архитектуре Svelte.
Другой способ интеграции заключается в использовании
bind:this и жизненного цикла onMount, что дает
полный контроль над экземпляром.
<script>
import { onMount, onDestroy } from "svelte";
import AutoNumeric from "autonumeric";
let input;
let auto;
let value = 1000;
onMount(() => {
auto = new AutoNumeric(input, {
decimalPlaces: 0,
digitGroupSeparator: " "
});
auto.se t(value);
input.addEventListener("input", sync);
});
function sync() {
value = auto.getNumber();
}
$: if (auto && value !== auto.getNumber()) {
auto.se t(value);
}
onDestroy(() => {
input.removeEventListener("input", sync);
auto.remove();
});
</script>
<input bind:this={input} />
<p>{value}</p>
Здесь реактивное выражение $: обеспечивает синхронизацию
внешних изменений value с состоянием AutoNumeric,
предотвращая рассинхронизацию при программных обновлениях.
Svelte позволяет динамически изменять конфигурацию AutoNumeric через реактивные переменные. Это особенно полезно для переключения локалей, валютных форматов или точности.
<script>
import { onMount } from "svelte";
import AutoNumeric from "autonumeric";
let input;
let auto;
let precision = 2;
let groupSeparator = " ";
onMount(() => {
auto = new AutoNumeric(input, {
decimalPlaces: precision,
digitGroupSeparator: groupSeparator
});
});
$: if (auto) {
auto.update({
decimalPlaces: precision,
digitGroupSeparator: groupSeparator
});
}
</script>
<input bind:this={input} />
Такой подход позволяет изменять формат без пересоздания экземпляра, что важно для производительности при частых обновлениях интерфейса.
Для крупных приложений рационально обернуть AutoNumeric в Svelte-компонент, скрывающий детали реализации.
<script>
import { onMount, onDestroy } from "svelte";
import AutoNumeric from "autonumeric";
export let value = 0;
export let options = {};
let input;
let instance;
onMount(() => {
instance = new AutoNumeric(input, options);
instance.set(value);
input.addEventListener("input", sync);
});
function sync() {
value = instance.getNumber();
}
$: if (instance && value !== instance.getNumber()) {
instance.set(value);
}
$: if (instance) {
instance.update(options);
}
onDestroy(() => {
input.removeEventListener("input", sync);
instance.remove();
});
</script>
<input bind:this={input} />
<style>
input {
width: 100%;
padding: 6px 10px;
}
</style>
Использование:
<NumericInput bind:value={price} options={{ decimalPlaces: 2 }} />
Главная сложность при работе AutoNumeric и Svelte заключается в
двусторонней синхронизации, которая может приводить к циклам обновлений.
Например, установка значения через instance.set() вызывает
событие input, которое снова обновляет Svelte-переменную.
Для устранения этого применяется флаг блокировки:
<script>
let locking = false;
function sync() {
if (locking) return;
value = instance.getNumber();
}
$: if (instance) {
locking = true;
instance.set(value);
locking = false;
}
</script>
Альтернативный подход — сравнение значений перед установкой, что уменьшает количество лишних операций.
AutoNumeric разделяет отображаемое значение и числовое представление. В Svelte важно явно оперировать только числом в состоянии.
getNumber() — возвращает чистое числоgetFormatted() — возвращает строку с
форматированиемset(value) — устанавливает значениеclear() — очищает полеИспользование строкового значения в реактивном состоянии приводит к рассинхронизации логики, поэтому модель данных должна оставаться числовой.
При изменении региональных настроек требуется обновление нескольких параметров одновременно:
$: if (auto && locale === "ru") {
auto.update({
decimalCharacter: ",",
digitGroupSeparator: " "
});
}
$: if (auto && locale === "en") {
auto.update({
decimalCharacter: ".",
digitGroupSeparator: ","
});
}
Такой механизм позволяет переключать представление без пересоздания input-элемента и потери фокуса.
При интеграции в формы Svelte важно учитывать, что AutoNumeric изменяет отображаемое значение, но не управляет валидацией HTML5 напрямую. Валидация должна выполняться на уровне числового состояния:
<script>
let value = 0;
$: isValid = value >= 0 && value <= 10000;
</script>
<input use:autoNumeric on:numeric-change={(e) => value = e.detail} />
{#if !isValid}
<span>Недопустимое значение</span>
{/if}
Такой подход разделяет ответственность: AutoNumeric отвечает за форматирование, Svelte — за бизнес-логику.
При частых обновлениях значений (например, в калькуляторах или
финансовых формах) важно минимизировать вызовы upd ate() и
set().
Рекомендуется:
Эти принципы снижают количество перерасчётов DOM и предотвращают лаги при вводе больших чисел
В SvelteKit или других SSR-сценариях AutoNumeric должен инициализироваться только на клиенте, поскольку он зависит от DOM.
<script>
import { browser } from "$app/environment";
import AutoNumeric from "autonumeric";
let input;
if (browser) {
// безопасная инициализация
}
</script>
Инициализация вне onMount приводит к ошибкам гидратации,
так как DOM-элемент еще не существует.
В формах с несколькими числовыми полями часто требуется централизованное управление экземплярами AutoNumeric. Это достигается через коллекцию:
const instances = new Map();
export function register(node, options) {
const instance = new AutoNumeric(node, options);
instances.se t(node, instance);
return instance;
}
export function destroy(node) {
const instance = instances.get(node);
if (instance) {
instance.remove();
instances.delete(node);
}
}
Такой подход позволяет управлять группами полей, синхронизировать валютные настройки и выполнять массовые обновления конфигурации.