插件的环境 API
实验性
环境 API 处于实验阶段。我们将在 Vite 6 期间保持 API 稳定,以便生态系统进行实验并在其之上构建。我们计划在 Vite 7 中使用潜在的重大更改来稳定这些新 API。
资源
请与我们分享您的反馈。
在钩子中访问当前环境
鉴于在 Vite 6 之前只有两个环境(client
和 ssr
),ssr
布尔值足以在 Vite API 中识别当前环境。插件钩子在最后一个选项参数中接收 ssr
布尔值,并且一些 API 期望一个可选的最后一个 ssr
参数以将模块正确地关联到正确的环境(例如 server.moduleGraph.getModuleByUrl(url, { ssr })
)。
随着可配置环境的出现,我们现在有了一种统一的方式来访问插件中其选项和实例。插件钩子现在在其上下文中公开 this.environment
,并且以前期望 ssr
布尔值的 API 现在作用域到正确的环境(例如 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 // Invalidate modules manually 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) => { // perform custom update }) }
每个环境的插件
插件可以使用 applyToEnvironment
函数定义其应应用到的环境。
const UnoCssPlugin = () => {
// shared global state
return {
buildStart() {
// init per environment state with WeakMap<Environment,Data>
// using this.environment
},
configureServer() {
// use global hooks normally
},
applyToEnvironment(environment) {
// return true if this plugin should be active in this environment,
// or return a new plugin to replace it.
// if the hook is not used, the plugin is active in all environments
},
resolveId(id, importer) {
// only called for environments this plugin apply to
},
}
}
如果插件没有环境感知并且其状态没有根据当前环境进行键控,则 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() {
// Share state among all environments in dev and build
const sharedState = ...
return {
name: 'shared-plugin',
transform(code, id) { ... },
// Opt-in into a single instance for all environments
sharedDuringBuild: true,
}
}