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 обрабатывает CSS как часть графа зависимостей. Tom Sel ect предоставляет несколько вариантов оформления:
tom-select.default.csstom-select.bootstrap4.csstom-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";
Хотя Tom Select не требует специального плагина Vite, в крупных приложениях полезно явно указать его в оптимизации зависимостей.
import { defineConfig } fr om "vite";
export default defineConfig({
optimizeDeps: {
include: ["tom-select"]
}
});
Это ускоряет dev-сервер за счёт предварительной сборки CommonJS/ESM гибридов.
Tom Sel ect в современных версиях поддерживает ESM, но в некоторых окружениях Vite может встречаться смешанный формат модулей.
Если возникают проблемы с импортом, применяется алиасизация:
export default defineConfig({
resolve: {
alias: {
"tom-select": "tom-select/dist/js/tom-select.complete.js"
}
}
});
Такой подход фиксирует поведение и исключает неоднозначную сборку.
В 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
});
}
Такой подход уменьшает начальный бандл и загружает библиотеку только при необходимости.
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>
);
}
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 };
}
};
При серверном рендеринге Tom Select должен инициализироваться только на клиенте, поскольку зависит от DOM.
Проверка окружения:
if (typeof window !== "undefined") {
import("tom-select").then(({ default: TomSelect }) => {
new TomSelect("#select");
});
}
В SSR-сценариях важно избегать прямого обращения к
document во время рендера.
optimizeDeps: {
include: ["tom-select"]
}
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ["tom-select"]
}
}
}
}
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;
Хотя Vite ориентирован на npm-зависимости, иногда используется внешний CDN:
import TomSelect fr om "https://cdn.skypack.dev/tom-select";
Такой подход ухудшает контроль сборки и кэширования, но может использоваться в прототипах.
При необходимости можно подключить пользовательский 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 зависимостей.
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;
}
}
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";
}
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"
}
}
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
}
}
При масштабировании приложения важно учитывать:
router.afterEach(() => {
document.querySelectorAll(".tomselect").forEach(el => {
if (el.tomselect) {
el.tomselect.destroy();
}
});
});