Plugin API
Vite плагины расширяют хорошо спроектированный интерфейс плагинов Rollup с некоторыми дополнительными Vite-specific опциями. Поэтому, вы можете один раз написать Vite плагин и он будет работать и в dev и в build.
Рекомендуется сначала прочитать Rollup's plugin documentation прежде чем продолжать чтение этой секции.
Соглашения
Если плагин не использует Vite specific hooks и может быть реализован как Compatible Rollup Plugin, то мы рекомендуем использовать Rollup Plugin naming conventions
- Rollup плагины должны иметь понятное имя с префиксом
rollup-plugin-
. - Включать
rollup-plugin
иvite-plugin
ключевые слова в package.json.
Это также позволяет использовать плагин в чистом Rollup или WMR based проектах
Для плагинов только для Vite
- Vite плагины должны иметь понятное имя с префиксом
vite-plugin-
. - Включать
vite-plugin
ключевое слово в package.json. - Иметь раздел документации плагина, детально описывающий почему этот плагин только для Vite (например, плагин использует Vite specific plugin hooks).
Если ваш плагин будет работать только с конкретным фреймворком, то название плагина должно включать в себя часть с префиксом этого фреймворка
vite-plugin-vue-
префикс для Vue плагиновvite-plugin-react-
префикс для React плагиновvite-plugin-svelte-
префикс для Svelte плагинов
Plugins config
Пользователи будут добавлять плагины вdevDependencies
проекта и настраивать их используя plugins
array option.
// vite.config.js
import vitePlugin from 'vite-plugin-feature'
import rollupPlugin from 'rollup-plugin-feature'
export default defineConfig({
plugins: [vitePlugin(), rollupPlugin()]
})
Falsy плагины будут игнорированы, что может быть использовано для лёгкой активации и деактивации плагинов.
plugins
также принимает preset'ы включающие несколько плагинов как один элемент. Это полезно для сложных фич (таких, как интеграция фреймворков), которая реализуется используя несколько плагинов. Массив будет внутренне "flattened" выровнен (элементы на одном уровне, без вложенности).
// framework-plugin
import frameworkRefresh from 'vite-plugin-framework-refresh'
import frameworkDevtools from 'vite-plugin-framework-devtools'
export default function framework(config) {
return [frameworkRefresh(config), frameworkDevTools(config)]
}
// vite.config.js
import { defineConfig } from 'vite'
import framework from 'vite-plugin-framework'
export default defineConfig({
plugins: [framework()]
})
Простые примеры
Подсказка
Общепринятым является создание Vite/Rollup плагина как factory function "функция фабрика", которая возвращает актуальный объект плагина. Функция может принимать параметры, которые позволяют пользователям кастомизировать поведение плагина.
Importing a Virtual File
export default function myPlugin() {
const virtualFileId = '@my-virtual-file'
return {
name: 'my-plugin', // обязательно, будет показано при warnings и errors
resolveId(id) {
if (id === virtualFileId) {
return virtualFileId
}
},
load(id) {
if (id === virtualFileId) {
return `export const msg = "from virtual file"`
}
}
}
}
Это позволяет нам импортировать файл в JavaScript:
import { msg } from '@my-virtual-file'
console.log(msg)
Трансформирование Custom File Types
const fileRegex = /\.(my-file-ext)$/
export default function myPlugin() {
return {
name: 'transform-file',
transform(src, id) {
if (fileRegex.test(id)) {
return {
code: compileFileToJS(src),
map: null // provide source map if available
}
}
}
}
}
Universal Hooks
В процессе разработки, Vite dtv сервер создаёт плагин контейнер, который вызывает Rollup Build Hooks Rollup делает это тем же способом.
Следующие hooks (хуки) вызываются один раз при запуске сервера:
Следующие hooks (хуки) вызываются при каждом входящем запросе модуля:
Следующие hooks (хуки) вызываются когда сервер потушен:
Обратите внимание, что moduleParsed
hook НЕ вызывается во время dev, потому что Vite избегает полного парсинга AST для улучшения производительности.
Output Generation Hooks (кроме closeBundle
) НЕ вызываются во время dev. Вы можете думать, что Vite's dev сервер вызывает только rollup.rollup()
без вызова bundle.generate()
.
Vite Specific Hooks
Vite плагины также могу предоставлять хуки, которые служат Vite-specific целям. Rollup игнорирует эти хуки.
config
Type:
(config: UserConfig, env: { mode: string, command: string }) => UserConfig | null | void
Kind:
async
,sequential
Изменяет конфигурацию Vite, прежде чем она будет резолвнута. Хук получает необработанный config (CLI option смёрдженные с конфиг файлом) и текущую конфигурацию env, которая exposes используемые
mode
иcommand
. Можете вернуть частичный объект конфига, который будет глубоко смёрджен (deeply merged) с существующей конфигурацией или напрямую изменить congif (если дефолтный merging не может достичь желаемого результата).Пример:
// вернуть частичный конфиг (рекомендуется) const partialConfigPlugin = () => ({ name: 'return-partial', config: () => ({ alias: { foo: 'bar' } }) }) // напрямую изменить конфиг (используйте только когда merging не работает) const mutateConfigPlugin = () => ({ name: 'mutate-config', config(config, { command }) { if (command === 'build') { config.root = __dirname } } })
Заметка
Плагины пользователей резолвятся до запуска этого хука, поэтому встраивание (injecting) других плагинов внутри
config
хука не приведёт ни к каким результатам.
configResolved
Type:
(config: ResolvedConfig) => void | Promise<void>
Kind:
async
,parallel
Вызывается после того, как Vite config резолвнется. Используйте этот хук, чтобы прочитать и сохранить финальный резолвнутый конфиг. Это также полезно, когда плагину нужно сделать что-нибудь другое в зависимости от выполняемой команды.
Пример:
const examplePlugin = () => { let config return { name: 'read-config', configResolved(resolvedConfig) { // сохранить резолвнутый (resolved) config config = resolvedConfig }, // потом можно использовать сохранённый store в других хуках transform(code, id) { if (config.command === 'serve') { // serve: plugin invoked by dev server } else { // build: plugin invoked by Rollup } } } }
configureServer
Type:
(server: ViteDevServer) => (() => void) | void | Promise<(() => void) | void>
Kind:
async
,sequential
Читайте также: ViteDevServer
Хук для настройки (конфигурирования) dev сервера. Самый популярный случай использования - это добавление кастомноых middlewares во внутренний connect app:
const myPlugin = () => ({ name: 'configure-server', configureServer(server) { server.middlewares.use((req, res, next) => { // custom handle request... }) } })
Injecting Post Middleware
configureServer
хук вызывается до того, как внутренние middlewares установлены, поэтому кастомные middlewares будут запущены до internal middlewares по умолчанию. Если вы хотите заинжектить middleware после internal middlewares, вы можете вернуть функцию изconfigureServer
, которая будет вызвана после того, как internal middlewares будут установлены:const myPlugin = () => ({ name: 'configure-server', configureServer(server) { // возвращаем post hook, который вызовется после того, как // internal middlewares будут установлены return () => { server.middlewares.use((req, res, next) => { // custom handle request... }) } } })
Storing Server Access
В некоторых случаях, другим хукам плагина могут понадобиться доступы до экземпляра dev сервера (например, доступ до web socket server, the file system watcher, или the module graph). Этот хук может быть использован для сохранения экземпляра сервера, чтобы потом обращаться к этому экземпляру из других хуков:
const myPlugin = () => { let server return { name: 'configure-server', configureServer(_server) { server = _server }, transform(code, id) { if (server) { // use server... } } } }
Заметьте,
configureServer
не вызывается когда запущен production build, поэтому другим вашим хукам необходимо подготовиться к этому, к отсутствию этого хука.
transformIndexHtml
Type:
IndexHtmlTransformHook | { enforce?: 'pre' | 'post' transform: IndexHtmlTransformHook }
Kind:
async
,sequential
Выделенный хук для трансформирования
index.html
. Этот хук получает текущую HTML строку и transform context. Context предоставляетViteDevServer
экземпляр во время dev (разработки) и output Rollup bundle для build.Этот хук может быть асинхронным и может возвращать одно из следующих значений:
- Трансформированную HTML строку
- Array of tag descriptor objects (
{ tag, attrs, children }
) , чтобы встроить их в существующий HTML. Каждый тег также может указать куда ему следует инжектиться (по умолчанию добавляется в<head>
) - Объект содержащий
{ html, tags }
Базовый пример:
const htmlPlugin = () => { return { name: 'html-transform', transformIndexHtml(html) { return html.replace( /<title>(.*?)<\/title>/, `<title>Title replaced!</title>` ) } } }
Full Hook Signature (полная сигнатура хука):
type IndexHtmlTransformHook = ( html: string, ctx: { path: string filename: string server?: ViteDevServer bundle?: import('rollup').OutputBundle chunk?: import('rollup').OutputChunk } ) => | IndexHtmlTransformResult | void | Promise<IndexHtmlTransformResult | void> type IndexHtmlTransformResult = | string | HtmlTagDescriptor[] | { html: string tags: HtmlTagDescriptor[] } interface HtmlTagDescriptor { tag: string attrs?: Record<string, string | boolean> children?: string | HtmlTagDescriptor[] /** * default: 'head-prepend' */ injectTo?: 'head' | 'body' | 'head-prepend' | 'body-prepend' }
handleHotUpdate
Type:
(ctx: HmrContext) => Array<ModuleNode> | void | Promise<Array<ModuleNode> | void>
Выполняет кастомную обработку HMR обновлений. Этот хук получает context object со следующей сигнатурой:
interface HmrContext { file: string timestamp: number modules: Array<ModuleNode> read: () => string | Promise<string> server: ViteDevServer }
modules
массив модулей, которые затронуты изменениями в файле. Это массив, потому что одиночный файл может мапиться с несколькими модулями, обрабатываемыми сервером (например, Vue SFCs).read
это асинхронная функция чтения, которая возвращает content файла. Это предоставляется потому что на некоторых системах, callback от изменений файла может происходить даже быстрее, чем пользователь успеет изменить файл и прямой методfs.readFile
вернёт пустой контент. Функция чтения нормализует это поведение.
Хук можно использовать для:
Фильтрации и отсечения ненужного списка затронутых модулей, чтобы HMR работал более точнее.
Возвращения пустого массива и выполнения полной пользовательской обработки HMR, отправив пользователю custom events:
handleHotUpdate({ server }) { server.ws.send({ type: 'custom', event: 'special-update', data: {} }) return [] }
Клиентский код должен зарегистрировать соответствующий обработчик используя HMR API (это может быть встроено с помощью хука
transform
этого же плагина):if (import.meta.hot) { import.meta.hot.on('special-update', (data) => { // perform custom update }) }
Порядок применения плагинов
Vite плагин может дополнительно указать свойство enforce
(похоже на webpack loaders) чтобы настроить порядок применения плагинов. Значение поля enforce
может быть "pre"
или "post"
. Резолвнутые плагины будут идти в следующем порядке:
- Alias
- Пользовательские плагины с
enforce: 'pre'
- Vite core плагины
- Пользовательские плагины без значения enforce
- Vite build плагины
- Пользовательские плагины с
enforce: 'post'
- Vite post build плагины (minify, manifest, reporting)
Условное применение
По умолчанию плагины выполняются и для serve и для build. В случае, где плагин нужно применить только на serve или build, используйте свойство apply
, чтобы запустить плагин только на 'build'
или 'serve'
:
function myPlugin() {
return {
name: 'build-only',
apply: 'build' // or 'serve'
}
}
Также для большего контроля можно использовать какую-нибудь функцию:
apply(config, { command }) {
// apply only on build but not for SSR
return command === 'build' && !config.build.ssr
}
Совместимость с Rollup плагинами
Изрядное количество подключаемых Rollup плагинов будут работать прямо как Vite плагины (например @rollup/plugin-alias
или @rollup/plugin-json
), но не все плагины работают так, поскольку некоторые хуки плагинов не имеют смысла в (несобранном) unbundled dev сервер контексте.
В общем, если плагин Rollup соответствует следующим критериям, то он должен работать как простой Vite плагин:
- Он не использует
moduleParsed
хук. - Он не имеет жёсткой связи с bundle-phase хуками и output-phase хуками.
Если Rollup плагин может быть применим только на build phase, то лучше указать его в build.rollupOptions.plugins
.
Вы также можете дополнить существующий Rollup плагин Vite-only свойствами:
// vite.config.js
import example from 'rollup-plugin-example'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
{
...example(),
enforce: 'post',
apply: 'build'
}
]
})
Ознакомьтесь с Vite Rollup Plugins - список официальных совместимых Rollup плагинов с пользовательскими инструкциями.
Нормализация путей
Vite нормализует пути при резолве id, чтобы использовать POSIX разделители ( / ) при сохранении volume в Windows. С другой стороны, Rollup оставляет нетронутыми пути по умолчанию, поэтому резолвнутые id имеют win32 разделители ( \ ) в Windows. Однако, Rollup плагины используют normalizePath
utility function из @rollup/pluginutils
внутренне, который преобразует разделители в POSIX до выполнения сравнения. Это значит, что когда эти плагины используются в Vite, include
и exclude
config pattern и другие аналогичные пути для сравнения резолвнутых id работают правильно.
Итак, для Vite плагинов, при сравнении резолвнутых путей id важно сначала нормализовать пути для использования POSIX разделителей. Эквивалентная утилитарная функция normalizePath
экспортируется из модуля vite
.
import { normalizePath } from 'vite'
normalizePath('foo\\bar') // 'foo/bar'
normalizePath('foo/bar') // 'foo/bar'