Vite конфигурация

Tom Sel ect в связке с Vite используется как легковесная замена стандартным селектам с расширенной функциональностью: поиск, мультивыбор, теги, асинхронные источники данных и кастомный рендеринг. В Vite-проекте библиотека интегрируется без дополнительных сборщиков, однако требует правильной настройки импорта стилей и оптимизации зависимостей.

Установка выполняется стандартно через пакетный менеджер:

npm install tom-select

или

yarn add tom-select

После установки библиотека становится доступной для ESM-импорта напрямую в модуле.


Базовый импорт и инициализация

Vite работает на нативных ES-модулях, поэтому Tom Sel ect подключается без дополнительных трансформаций:

import TomSelect fr om "tom-select";
import "tom-select/dist/css/tom-select.default.css";

const sel ect = new TomSelect("#select", {
  create: true,
  sortField: "text"
});

Ключевой момент — обязательный импорт CSS. Без него компонент будет функционален, но визуально не оформлен.


Подключение стилей в Vite

Vite обрабатывает CSS как часть графа зависимостей. Tom Sel ect предоставляет несколько вариантов оформления:

  • tom-select.default.css
  • tom-select.bootstrap4.css
  • tom-select.bootstrap5.css

Пример подключения темы Bootstrap 5:

import "tom-select/dist/css/tom-select.bootstrap5.css";

При необходимости можно комбинировать с глобальными стилями проекта:

/* styles/global.css */
.tom-select {
  font-size: 14px;
  border-radius: 8px;
}

И импортировать в entry-point:

import "./styles/global.css";

Vite конфигурация и оптимизация зависимостей

Хотя Tom Select не требует специального плагина Vite, в крупных приложениях полезно явно указать его в оптимизации зависимостей.

vite.config.js

import { defineConfig } fr om "vite";

export default defineConfig({
  optimizeDeps: {
    include: ["tom-select"]
  }
});

Это ускоряет dev-сервер за счёт предварительной сборки CommonJS/ESM гибридов.


Работа с CommonJS и ESM нюансами

Tom Sel ect в современных версиях поддерживает ESM, но в некоторых окружениях Vite может встречаться смешанный формат модулей.

Если возникают проблемы с импортом, применяется алиасизация:

export default defineConfig({
  resolve: {
    alias: {
      "tom-select": "tom-select/dist/js/tom-select.complete.js"
    }
  }
});

Такой подход фиксирует поведение и исключает неоднозначную сборку.


Динамическая инициализация в Vite

В Vite часто используется ленивый рендеринг компонентов, особенно при работе с SPA и компонентными фреймворками.

Пример динамической инициализации:

async function initSelect() {
  const { default: TomSelect } = await import("tom-select");
  await import("tom-select/dist/css/tom-select.default.css");

  new TomSelect("#dynamic-select", {
    maxItems: 5,
    create: true
  });
}

Такой подход уменьшает начальный бандл и загружает библиотеку только при необходимости.


Использование в компонентах (React/Vue/Svelte через Vite)

React-стиль интеграции

import { useEffect, useRef } fr om "react";
import TomSelect fr om "tom-select";
import "tom-select/dist/css/tom-select.default.css";

export default function SelectComponent() {
  const ref = useRef(null);

  useEffect(() => {
    if (!ref.current) return;

    const instance = new TomSelect(ref.current, {
      create: true
    });

    return () => instance.destroy();
  }, []);

  return (
    <sel ect ref={ref}>
      <option value="1">One</option>
      <option value="2">Two</option>
    </select>
  );
}

Vue 3 (Vite-native)

import { onMounted, ref } fr om "vue";
import TomSelect fr om "tom-select";
import "tom-select/dist/css/tom-select.bootstrap5.css";

export default {
  setup() {
    const sel ect = ref(null);

    onMounted(() => {
      new TomSelect(sel ect.value, {
        persist: false
      });
    });

    return { select };
  }
};

SSR и гидрация (Vite SSR)

При серверном рендеринге Tom Select должен инициализироваться только на клиенте, поскольку зависит от DOM.

Проверка окружения:

if (typeof window !== "undefined") {
  import("tom-select").then(({ default: TomSelect }) => {
    new TomSelect("#select");
  });
}

В SSR-сценариях важно избегать прямого обращения к document во время рендера.


Оптимизация производительности в Vite

1. Предварительная загрузка модулей

optimizeDeps: {
  include: ["tom-select"]
}

2. Разделение чанков

build: {
  rollupOptions: {
    output: {
      manualChunks: {
        vendor: ["tom-select"]
      }
    }
  }
}

3. Ленивая инициализация инстансов

let instance;

function init() {
  if (instance) return;

  instance = new TomSelect("#select", {
    preload: false
  });
}

Работа с несколькими экземплярами

Vite-проекты часто используют компонентную архитектуру, где селекты создаются динамически:

document.querySelectorAll(".js-select").forEach((el) => {
  new TomSelect(el, {
    create: true,
    sortField: "text"
  });
});

Важно контролировать уничтожение экземпляров при удалении DOM:

instance.destroy();
instance = null;

Импорт через CDN внутри Vite (редкий сценарий)

Хотя Vite ориентирован на npm-зависимости, иногда используется внешний CDN:

import TomSelect fr om "https://cdn.skypack.dev/tom-select";

Такой подход ухудшает контроль сборки и кэширования, но может использоваться в прототипах.


Расширение поведения через плагины Vite

При необходимости можно подключить пользовательский Vite-плагин для автоматической инициализации селектов:

export default function tomSelectPlugin() {
  return {
    name: "tom-select-plugin",
    transformIndexHtml(html) {
      return html.replace(
        "</body>",
        `<script type="module">
          import TomSelect fr om "tom-select";
          document.querySelectorAll('.ts').forEach(el => new TomSelect(el));
        </script></body>`
      );
    }
  };
}

Алиасы и монорепозитории

В монорепозиториях Vite часто требует точной настройки резолва:

resolve: {
  alias: {
    "tom-select": "/node_modules/tom-select/dist/js/tom-select.complete.js"
  }
}

Это устраняет конфликты при hoisting зависимостей.


Типизация в TypeScript-проектах на Vite

Tom Sel ect предоставляет типы, но при необходимости их можно уточнить:

import TomSelect fr om "tom-select";

const select: TomSelect = new TomSelect("#select", {
  create: true
});

Дополнительно можно расширять интерфейсы:

declare module "tom-select" {
  interface TomSelectOptions {
    customOption?: boolean;
  }
}

Горячая перезагрузка (HMR) и Tom Select

Vite поддерживает HMR, однако Tom Select не всегда корректно обновляет DOM без пересоздания экземпляра.

Рекомендуется ручной контроль:

if (import.meta.hot) {
  import.meta.hot.accept(() => {
    instance.destroy();
    instance = new TomSelect("#select");
  });
}

Обработка ошибок и защита от двойной инициализации

В dev-режиме возможны повторные вызовы:

if (!el.dataset.tsInitialized) {
  new TomSelect(el, { create: true });
  el.dataset.tsInitialized = "true";
}

Асинхронные данные в Vite-среде

new TomSelect("#select", {
  load: function(query, callback) {
    fetch(`/api/search?q=${query}`)
      .then(res => res.json())
      .then(data => callback(data))
      .catch(() => callback());
  }
});

Vite не влияет на этот механизм напрямую, но ускоряет сетевую разработку за счёт proxy-конфигурации:

server: {
  proxy: {
    "/api": "http://localhost:3000"
  }
}

Работа с env-переменными

Vite использует import.meta.env, что удобно для конфигурации Tom Select:

new TomSelect("#select", {
  maxItems: import.meta.env.VITE_MAX_ITEMS
});

Кэширование и производственные сборки

В production-режиме Vite оптимизирует импорт Tom Select в отдельный чанк. При необходимости можно дополнительно управлять этим через Rollup:

build: {
  chunkSizeWarningLimit: 800,
  rollupOptions: {
    treeshake: true
  }
}

Использование в крупных интерфейсах

При масштабировании приложения важно учитывать:

  • изоляцию экземпляров Tom Select в компонентах
  • уничтожение инстансов при навигации
  • единый стиль импортов CSS
  • предотвращение повторной инициализации в SPA-роутерах
router.afterEach(() => {
  document.querySelectorAll(".tomselect").forEach(el => {
    if (el.tomselect) {
      el.tomselect.destroy();
    }
  });
});