跳转到内容

使用 Environment 实例

候选发布版本 (Release Candidate)

环境 API 目前处于候选发布阶段。我们将在主要版本之间保持 API 的稳定性,以便生态系统能够进行实验和构建。但请注意,某些特定 API 仍被视为实验性的。

一旦下游项目有时间试验这些新功能并完成验证,我们计划在未来的主要版本中稳定这些新 API(可能会有破坏性变更)。

资源

请与我们分享您的反馈。

访问环境

在开发过程中,可以通过 server.environments 访问开发服务器中可用的环境。

js
// create the server, or get it from the configureServer hook
const server = await createServer(/* options */)

const clientEnvironment = server.environments.client
clientEnvironment.transformRequest(url)
console.log(server.environments.ssr.moduleGraph)

你也可以在插件中访问当前环境。更多详情,请参阅插件的环境 API

DevEnvironment

在开发过程中,每个环境都是 DevEnvironment 类的一个实例。

ts
class DevEnvironment {
  /**
   * Unique identifier for the environment in a Vite server.
   * By default Vite exposes 'client' and 'ssr' environments.
   */
  name: string
  /**
   * Communication channel to send and receive messages from the
   * associated module runner in the target runtime.
   */
  hot: NormalizedHotChannel
  /**
   * Graph of module nodes, with the imported relationship between
   * processed modules and the cached result of the processed code.
   */
  moduleGraph: EnvironmentModuleGraph
  /**
   * Resolved plugins for this environment, including the ones
   * created using the per-environment `create` hook
   */
  plugins: Plugin[]
  /**
   * Allows to resolve, load, and transform code through the
   * environment plugins pipeline
   */
  pluginContainer: EnvironmentPluginContainer
  /**
   * Resolved config options for this environment. Options at the server
   * global scope are taken as defaults for all environments, and can
   * be overridden (resolve conditions, external, optimizedDeps)
   */
  config: ResolvedConfig & ResolvedDevEnvironmentOptions

  constructor(
    name: string,
    config: ResolvedConfig,
    context: DevEnvironmentContext,
  )

  /**
   * Resolve the URL to an id, load it, and process the code using the
   * plugins pipeline. The module graph is also updated.
   */
  async transformRequest(url: string): Promise<TransformResult | null>

  /**
   * Register a request to be processed with low priority. This is useful
   * to avoid waterfalls. The Vite server has information about the
   * imported modules by other requests, so it can warmup the module graph
   * so the modules are already processed when they are requested.
   */
  async warmupRequest(url: string): Promise<void>
}

其中 DevEnvironmentContext

ts
interface DevEnvironmentContext {
  hot: boolean
  transport?: HotChannel | WebSocketServer
  options?: EnvironmentOptions
  remoteRunner?: {
    inlineSourceMap?: boolean
  }
  depsOptimizer?: DepsOptimizer
}

TransformResult

ts
interface TransformResult {
  code: string
  map: SourceMap | { mappings: '' } | null
  etag?: string
  deps?: string[]
  dynamicDeps?: string[]
}

Vite 服务器中的环境实例允许你通过 environment.transformRequest(url) 方法来处理 URL。此函数将使用插件流水线将 url 解析为模块 id,加载它(从文件系统读取文件或通过实现虚拟模块的插件读取),然后转换代码。在转换模块时,导入及其他元数据将通过创建或更新相应的模块节点记录在环境模块图中。处理完成后,转换结果也会存储在模块中。

transformRequest 的命名

我们在当前提案版本中使用 transformRequest(url)warmupRequest(url),以便于习惯 Vite 现有 API 的用户进行讨论和理解。在正式发布之前,我们可以借此机会审视这些名称。例如,它可以命名为 environment.processModule(url)environment.loadModule(url),借鉴 Rollup 插件钩子中的 context.load(id)。目前,我们认为保留现有名称并推迟此讨论是更好的选择。

独立的模块图

每个环境都有一个独立的模块图。所有模块图都有相同的签名,因此可以实现通用算法来遍历或查询图,而无需依赖于具体的环境。hotUpdate 就是一个很好的例子。当文件被修改时,每个环境的模块图都将被用来发现受影响的模块,并为每个环境独立执行 HMR。

信息

Vite v5 混合了 Client 和 SSR 模块图。给定一个未处理或失效的节点,无法得知它对应的是 Client 环境、SSR 环境还是两者皆是。模块节点有一些带前缀的属性,如 clientImportedModulesssrImportedModules(以及返回两者并集的 importedModules)。importers 包含了每个模块节点来自 Client 和 SSR 环境的所有导入者。模块节点还拥有 transformResultssrTransformResult。向后兼容层允许生态系统从已弃用的 server.moduleGraph 进行迁移。

每个模块由 EnvironmentModuleNode 实例表示。模块可以在尚未处理的情况下注册到图中(在这种情况下 transformResultnull)。模块处理完成后,importersimportedModules 也会随之更新。

ts
class EnvironmentModuleNode {
  environment: string

  url: string
  id: string | null = null
  file: string | null = null

  type: 'js' | 'css'

  importers = new Set<EnvironmentModuleNode>()
  importedModules = new Set<EnvironmentModuleNode>()
  importedBindings: Map<string, Set<string>> | null = null

  info?: ModuleInfo
  meta?: Record<string, any>
  transformResult: TransformResult | null = null

  acceptedHmrDeps = new Set<EnvironmentModuleNode>()
  acceptedHmrExports: Set<string> | null = null
  isSelfAccepting?: boolean
  lastHMRTimestamp = 0
  lastInvalidationTimestamp = 0
}

environment.moduleGraphEnvironmentModuleGraph 的一个实例

ts
export class EnvironmentModuleGraph {
  environment: string

  urlToModuleMap = new Map<string, EnvironmentModuleNode>()
  idToModuleMap = new Map<string, EnvironmentModuleNode>()
  etagToModuleMap = new Map<string, EnvironmentModuleNode>()
  fileToModulesMap = new Map<string, Set<EnvironmentModuleNode>>()

  constructor(
    environment: string,
    resolveId: (url: string) => Promise<PartialResolvedId | null>,
  )

  async getModuleByUrl(
    rawUrl: string,
  ): Promise<EnvironmentModuleNode | undefined>

  getModuleById(id: string): EnvironmentModuleNode | undefined

  getModulesByFile(file: string): Set<EnvironmentModuleNode> | undefined

  onFileChange(file: string): void

  onFileDelete(file: string): void

  invalidateModule(
    mod: EnvironmentModuleNode,
    seen: Set<EnvironmentModuleNode> = new Set(),
    timestamp: number = monotonicDateNow(),
    isHmr: boolean = false,
  ): void

  invalidateAll(): void

  async ensureEntryFromUrl(
    rawUrl: string,
    setIsSelfAccepting = true,
  ): Promise<EnvironmentModuleNode>

  createFileOnlyEntry(file: string): EnvironmentModuleNode

  async resolveUrl(url: string): Promise<ResolvedUrl>

  updateModuleTransformResult(
    mod: EnvironmentModuleNode,
    result: TransformResult | null,
  ): void

  getModuleByEtag(etag: string): EnvironmentModuleNode | undefined
}