Интеграция с бэкендом
Примечание
Если вы хотите обслуживать HTML с помощью традиционного бэкенда (например, Rails, Laravel), но использовать Vite для обслуживания ресурсов, проверьте существующие интеграции, перечисленные в Awesome Vite.
Если вам нужна пользовательская интеграция, вы можете следовать шагам в этом руководстве, чтобы настроить её вручную.
В вашей конфигурации Vite настройте точку входа и включите манифест сборки:
jsexport defaultdefineConfig({server: {cors: { // источник, к которому вы будете обращаться через браузерorigin: 'http://my-backend.example.com', }, },build: { // генерация .vite/manifest.json в outDirmanifest: true,rollupOptions: { // перезапись стандартной точки входа .htmlinput: '/path/to/main.js', }, }, })Если вы не отключили полифилл предварительной загрузки модулей, вам также нужно импортировать полифилл в вашу точку входа.
js// добавление начала входной точки вашего приложения import 'vite/modulepreload-polyfill'Для разработки вставьте следующее в HTML-шаблон вашего сервера (замените
http://localhost:5173на локальный URL, по которому работает Vite):html<!-- в режиме разработки --> <script type="module" src="http://localhost:5173/@vite/client"></script> <script type="module" src="http://localhost:5173/main.js"></script>Чтобы правильно обслуживать ресурсы, у вас есть два варианта:
- Убедитесь, что сервер настроен на проксирование запросов статических ресурсов к серверу Vite
- Установите
server.origin, чтобы сгенерированные URL-адреса ресурсов разрешались с использованием URL-адреса бэкенд-сервера вместо относительного пути
Это необходимо для правильной загрузки ресурсов, таких как изображения.
Обратите внимание, что если вы используете React с
@vitejs/plugin-react, вам также нужно добавить это перед вышеуказанными скриптами, так как плагин не может изменить HTML, который вы обслуживаете (заменитеhttp://localhost:5173на локальный URL, по которому работает Vite):html<script type="module"> import RefreshRuntime from 'http://localhost:5173/@react-refresh' RefreshRuntime.injectIntoGlobalHook(window) window.$RefreshReg$ = () => {} window.$RefreshSig$ = () => (type) => type window.__vite_plugin_react_preamble_installed__ = true </script>Для продакшен-сборки, после выполнения команды
vite build, будет сгенерирован файл.vite/manifest.jsonвместе с другими файлами ресурсов. Пример файла манифеста выглядит следующим образом:json{ "_shared-B7PI925R.js": { "file": "assets/shared-B7PI925R.js", "name": "shared", "css": ["assets/shared-ChJ_j-JJ.css"] }, "_shared-ChJ_j-JJ.css": { "file": "assets/shared-ChJ_j-JJ.css", "src": "_shared-ChJ_j-JJ.css" }, "logo.svg": { "file": "assets/logo-BuPIv-2h.svg", "src": "logo.svg" }, "baz.js": { "file": "assets/baz-B2H3sXNv.js", "name": "baz", "src": "baz.js", "isDynamicEntry": true }, "views/bar.js": { "file": "assets/bar-gkvgaI9m.js", "name": "bar", "src": "views/bar.js", "isEntry": true, "imports": ["_shared-B7PI925R.js"], "dynamicImports": ["baz.js"] }, "views/foo.js": { "file": "assets/foo-BRBmoGS9.js", "name": "foo", "src": "views/foo.js", "isEntry": true, "imports": ["_shared-B7PI925R.js"], "css": ["assets/foo-5UjPuW-k.css"] } }Манифест имеет структуру
Record<name, chunk>, где каждый чанк соответствует интерфейсуManifestChunk:tsinterface ManifestChunk { /** * Имя исходного файла этого чанка / ресурса, если известно */ src?: string /** * Имя выходного файла этого чанка / ресурса */ file: string /** * Список CSS-файлов, импортируемых этим чанком * * Это поле присутствует только в JS-чанках. */ css?: string[] /** * Список файлов-ресурсов (кроме CSS), импортируемых этим чанком * * Это поле присутствует только в JS-чанках. */ assets?: string[] /** * Является ли этот чанк или ресурс точкой входа */ isEntry?: boolean /** * Имя этого чанка / ресурса, если известно */ name?: string /** * Является ли этот чанк динамической точкой входа * * Это поле присутствует только в JS-чанках. */ isDynamicEntry?: boolean /** * Список статически импортируемых чанков этим чанком * * Значения — это ключи манифеста. Поле присутствует только в JS-чанках. */ imports?: string[] /** * Список динамически импортируемых чанков этим чанком * * Значения — это ключи манифеста. Поле присутствует только в JS-чанках. */ dynamicImports?: string[] }Каждая запись в манифесте представляет собой что-то одно из следующего списка:
- Чанки точек входа: Генерируются из файлов, указанных в
build.rollupOptions.input. Эти чанки имеютisEntry: true, а их ключ — это относительный путь src от корня проекта. - Динамические чанки точек входа: Генерируются из динамических импортов. Эти чанки имеют
isDynamicEntry: true, а их ключ — это относительный путь src от корня проекта. - Чанки, не являющиеся точками входа: Их ключ — это базовое имя сгенерированного файла с префиксом
_. - Чанки активов: Генерируются из импортированных активов, таких как изображения, шрифты. Их ключ — это относительный путь src от корня проекта.
- **CSS-файлы: Когда
build.cssCodeSplitравноfalse, генерируется единый CSS-файл с ключомstyle.css. Когдаbuild.cssCodeSplitне равноfalse, ключ генерируется аналогично JS-чанкам (т. е. чанки точек входа не будут иметь префикс_, а чанки, не являющиеся точками входа, будут иметь префикс_).
JS-чанки (чанки, не являющиеся ресурсами или CSS) будут содержать информацию о своих статических и динамических импортах (это ключи, соответствующие нужным чанкам в манифесте), а также о связанных с ними CSS-файлах и файлах-ресурсах (если они есть).
- Чанки точек входа: Генерируются из файлов, указанных в
Вы можете использовать этот файл для рендеринга ссылок или директив предварительной загрузки с хешированными именами файлов.
Вот пример HTML-шаблона для рендеринга правильных ссылок. Синтаксис здесь приведен только для объяснения, замените его на язык шаблонов вашего сервера. Функция
importedChunksприведена для иллюстрации и не предоставляется Vite.html<!-- в режиме разработки --> <!-- для cssFile из manifest[name].css --> <link rel="stylesheet" href="/{{ cssFile }}" /> <!-- для чанка из importedChunks(manifest, name) --> <!-- для cssFile из chunk.css --> <link rel="stylesheet" href="/{{ cssFile }}" /> <script type="module" src="/{{ manifest[name].file }}"></script> <!-- для чанка из importedChunks(manifest, name) --> <link rel="modulepreload" href="/{{ chunk.file }}" />В частности, бэкенд, генерирующий HTML, должен включать следующие теги, учитывая файл манифеста и точку входа:
Обратите внимание, что рекомендуется следовать этому порядку для оптимальной производительности:
- Тег
<link rel="stylesheet">для каждого файла в спискеcssчанка точки входа (если он существует). - Рекурсивно следовать всем чанкам в списке
importsточки входа и включать тег<link rel="stylesheet">для каждого CSS-файла из спискаcssкаждого импортированного чанка (если он существует). - Тег для ключа
fileчанка точки входа. Это может быть<script type="module">для JavaScript или<link rel="stylesheet">для CSS. - Опционально, тег
<link rel="modulepreload">для ключаfileкаждого импортированного JavaScript-чанка, снова рекурсивно следуя импорту, начиная с чанка точки входа.
Следуя приведённому выше примеру манифеста, для точки входа
main.jsв продакшен-сборке должны быть включены следующие теги:html<link rel="stylesheet" href="assets/main.b82dbe22.css" /> <link rel="stylesheet" href="assets/shared.a834bfc3.css" /> <script type="module" src="assets/main.4889e940.js"></script> <!-- опционально --> <link rel="modulepreload" href="assets/shared.83069a53.js" />В то время как следующее должно быть включено для точки входа
views/foo.js:html<link rel="stylesheet" href="assets/shared.a834bfc3.css" /> <script type="module" src="assets/foo.869aea0d.js"></script> <!-- опционально --> <link rel="modulepreload" href="assets/shared.83069a53.js" />Псевдо-реализация
importedChunksПример псевдо-реализации
importedChunksна TypeScript (это потребуется адаптировать для вашего языка программирования и языка шаблонов):tsimport type { Manifest, ManifestChunk } from 'vite' export default function importedChunks( manifest: Manifest, name: string, ): ManifestChunk[] { const seen = new Set<string>() function getImportedChunks(chunk: ManifestChunk): ManifestChunk[] { const chunks: ManifestChunk[] = [] for (const file of chunk.imports ?? []) { const importee = manifest[file] if (seen.has(file)) { continue } seen.add(file) chunks.push(...getImportedChunks(importee)) chunks.push(importee) } return chunks } return getImportedChunks(manifest[name]) }- Тег