Environment API для плагинов
Экспериментально
Environment API является экспериментальным. Мы будем поддерживать стабильность API в Vite 6, чтобы дать экосистеме возможность экспериментировать и строить на его основе. Мы планируем стабилизировать эти новые API с возможными изменениями, нарушающими обратную совместимость, в Vite 7.
Ресурсы:
- Обсуждение отзывов, где мы собираем отзывы о новых API.
- PR Environment API, где новый API был реализован и рассмотрен.
Пожалуйста, поделитесь с нами своим мнением.
Доступ к текущему окружению в хуках
Учитывая, что до Vite 6 существовало только два окружения (client
и ssr
), булевый параметр ssr
был достаточен для идентификации текущего окружения в API Vite. Хуки плагинов получали булевый параметр ssr
в последнем параметре опций, и несколько API ожидали необязательный последний параметр ssr
, чтобы правильно ассоциировать модули с соответствующим окружением (например, server.moduleGraph.getModuleByUrl(url, { ssr })
).
С появлением настраиваемых окружений у нас теперь есть единый способ доступа к их параметрам и экземплярам в плагинах. Хуки плагинов теперь предоставляют this.environment
в своем контексте, а API, которые ранее ожидали булевый параметр ssr
, теперь ограничены соответствующим окружением (например, environment.moduleGraph.getModuleByUrl(url)
).
Сервер Vite имеет общий конвейер плагинов, но когда модуль обрабатывается, это всегда происходит в контексте данного окружения. Экземпляр environment
доступен в контексте плагина.
Плагин может использовать экземпляр environment
, чтобы изменить способ обработки модуля в зависимости от конфигурации для окружения (которую можно получить с помощью environment.config
).
transform(code, id) {
console.log(this.environment.config.resolve.conditions)
}
Регистрация новых окружений с помощью хуков
Плагины могут добавлять новые окружения в хуке config
(например, чтобы иметь отдельный граф модулей для RSC):
config(config: UserConfig) {
config.environments.rsc ??= {}
}
Пустого объекта достаточно для регистрации окружения, значения по умолчанию берутся из конфигурации окружения на корневом уровне.
Настройка окружения с помощью хуков
Во время выполнения хука config
полный список окружений ещё не известен, и на окружения могут влиять как значения по умолчанию из конфигурации окружения на корневом уровне, так и явно через запись config.environments
. Плагины должны устанавливать значения по умолчанию с помощью хука config
. Чтобы настроить каждое окружение, они могут использовать новый хук configEnvironment
. Этот хук вызывается для каждого окружения с его частично разрешённой конфигурацией, включая разрешение окончательных значений по умолчанию.
configEnvironment(name: string, options: EnvironmentOptions) {
if (name === 'rsc') {
options.resolve.conditions = // ...
Хук hotUpdate
- Тип:
(this: { environment: DevEnvironment }, options: HotUpdateOptions) => Array<EnvironmentModuleNode> | void | Promise<Array<EnvironmentModuleNode> | void>
- См. также: HMR API
Хук hotUpdate
позволяет плагинам выполнять пользовательскую обработку обновлений HMR для данного окружения. Когда файл изменяется, алгоритм HMR выполняется для каждого окружения последовательно в соответствии с порядком в server.environments
, поэтому хук hotUpdate
будет вызываться несколько раз. Хук получает объект контекста со следующей сигнатурой:
interface HotUpdateOptions {
type: 'create' | 'update' | 'delete'
file: string
timestamp: number
modules: Array<EnvironmentModuleNode>
read: () => string | Promise<string>
server: ViteDevServer
}
this.environment
— это среда выполнения модуля, в которой в настоящее время обрабатывается обновление файла.modules
— это массив модулей в этом окружении, которые затронуты изменённым файлом. Это массив, потому что один файл может соответствовать нескольким обслуживаемым модулям (например, Vue SFC).read
— это асинхронная функция чтения, которая возвращает содержимое файла. Это предоставляется потому, что на некоторых системах обратный вызов изменения файла может сработать слишком быстро, прежде чем редактор завершит обновление файла, и прямой вызовfs.readFile
вернет пустое содержимое. Функция чтения, переданная в хук, нормализует это поведение.
Хук может выбрать:
Отфильтровать и уточнить список затронутых модулей, чтобы HMR был более точным.
Вернуть пустой массив и выполнить полную перезагрузку:
jshotUpdate({ modules, timestamp }) { if (this.environment.name !== 'client') return // Ручная инвалидация модулей const invalidatedModules = new Set() for (const mod of modules) { this.environment.moduleGraph.invalidateModule( mod, invalidatedModules, timestamp, true ) } this.environment.hot.send({ type: 'full-reload' }) return [] }
Вернуть пустой массив и выполнить полную пользовательскую обработку HMR, отправляя пользовательские события клиенту:
jshotUpdate() { if (this.environment.name !== 'client') return this.environment.hot.send({ type: 'custom', event: 'special-update', data: {} }) return [] }
Код клиента должен зарегистрировать соответствующий обработчик, используя HMR API (это может быть внедрено через хук
transform
того же плагина):jsif (import.meta.hot) { import.meta.hot.on('special-update', (data) => { // пользовательское обновление }) }
Плагины для каждого окружения
Плагин может определить, к каким окружениям он должен применяться, с помощью функции applyToEnvironment
.
const UnoCssPlugin = () => {
// общее глобальное состояние
return {
buildStart() {
// инициализация состояния для каждого окружения с WeakMap<Environment,Data>
// с использованием this.environment
},
configureServer() {
// используйте глобальные хуки как обычно
},
applyToEnvironment(environment) {
// верните true, если этот плагин должен быть активен в этом окружении,
// или верните новый плагин, чтобы заменить его.
// если хук не используется, плагин активен во всех окружениях
},
resolveId(id, importer) {
// вызывается только для окружений, к которым этот плагин применяется
},
}
}
Если плагин не учитывает окружение и имеет состояние, которое не связано с текущим окружением, хук applyToEnvironment
позволяет легко сделать его специфичным для каждого окружения.
import { nonShareablePlugin } from 'non-shareable-plugin'
export default defineConfig({
plugins: [
{
name: 'per-environment-plugin',
applyToEnvironment(environment) {
return nonShareablePlugin({ outputName: environment.name })
},
},
],
})
Vite экспортирует вспомогательную функцию perEnvironmentPlugin
, чтобы упростить такие случаи, когда не требуются другие хуки:
import { nonShareablePlugin } from 'non-shareable-plugin'
export default defineConfig({
plugins: [
perEnvironmentPlugin('per-environment-plugin', (environment) =>
nonShareablePlugin({ outputName: environment.name }),
),
],
})
Окружение в хуках сборки
Так же, как и во время разработки, хуки плагинов также получают экземпляр окружения во время сборки, заменяя булевый параметр ssr
. Это также работает для renderChunk
, generateBundle
и других хуков, которые используются только во время сборки.
Общие плагины во время сборки
До Vite 6 конвейеры плагинов работали по-разному во время разработки и сборки:
- Во время разработки: плагины общие
- Во время сборки: плагины изолированы для каждого окружения (в разных процессах:
vite build
, затемvite build --ssr
).
Это заставляло фреймворки делиться состоянием между сборкой client
и сборкой ssr
через манифесты, записанные в файловую систему. В Vite 6 мы теперь собираем все окружения в одном процессе, поэтому способ работы конвейера плагинов и коммуникации между окружениями может быть согласован с разработкой.
В будущем крупном обновлении (Vite 7 или 8) мы стремимся к полной согласованности:
- Во время разработки и сборки: плагины общие, с фильтрацией по окружению
Также будет единственный экземпляр ResolvedConfig
, который будет общим во время сборки, что позволит кэшировать на уровне всего процесса сборки приложения так же, как мы делали это с WeakMap<ResolvedConfig, CachedData>
во время разработки.
Для Vite 6 нам нужно сделать небольшой шаг, чтобы сохранить обратную совместимость. Плагины экосистемы в настоящее время используют config.build
вместо environment.config.build
для доступа к конфигурации, поэтому нам нужно создать новый ResolvedConfig
по умолчанию для каждого окружения. Проект может выбрать возможность совместного использования полной конфигурации и конвейера плагинов, установив builder.sharedConfigBuild
в true
.
Эта опция будет работать только для небольшой части проектов в начале, поэтому авторы плагинов могут выбрать, чтобы конкретный плагин был общим, установив флаг sharedDuringBuild
в true
. Это позволяет легко делиться состоянием как для обычных плагинов:
function myPlugin() {
// Делимся состоянием между всеми окружениями как во время разработки, так и во время сборки
const sharedState = ...
return {
name: 'shared-plugin',
transform(code, id) { ... },
// Выбор единственного экземпляра для всех окружений
sharedDuringBuild: true,
}
}