Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 50 additions & 71 deletions guide/api-environment-frameworks.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@
请与我们分享您的反馈。
:::

## 环境和框架 {#environments-and-frameworks}
## 开发环境通信级别

隐式的 `ssr` 环境和其他非客户端环境在开发过程中默认使用 `RunnableDevEnvironment`。虽然这要求运行时与 Vite 服务器运行的环境相同,但这与 `ssrLoadModule` 类似,允许框架迁移并为其 SSR 开发方案启用模块热替换(HMR)。你可以使用 `isRunnableDevEnvironment` 函数来保护任何可运行的环境。
由于环境可能在不同的运行时环境中运行,与环境的通信可能会受到运行时环境的限制。为了使框架能够轻松编写与运行时环境无关的代码,环境 API 提供了三种通信级别。

### `RunnableDevEnvironment`

`RunnableDevEnvironment` 是一种能够传递任意值的环境。隐式 `ssr` 环境及其他非客户端环境在开发阶段默认使用 `RunnableDevEnvironment`。虽然这要求运行时与 Vite 服务器运行的环境一致,但其工作原理与 `ssrLoadModule` 类似,并允许框架迁移并为其 SSR 开发流程启用 HMR。您可以通过 `isRunnableDevEnvironment` 函数对任何可运行环境进行保护。

```ts
export class RunnableDevEnvironment extends DevEnvironment {
Expand Down Expand Up @@ -43,50 +47,7 @@ if (isRunnableDevEnvironment(server.environments.ssr)) {
只有在第一次使用时,`runner` 才会被加载。请注意,当通过调用 `process.setSourceMapsEnabled` 或在不支持的情况下重写 `Error.prepareStackTrace` 创建 `runner` 时,Vite 会启用源映射支持。
:::

那些通过 [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch) 与它们的运行环境进行交互的框架可以使用 `FetchableDevEnvironment`,它提供了一种标准化的方式来通过 `handleRequest` 方法处理请求:

```ts
import {
createServer,
createFetchableDevEnvironment,
isFetchableDevEnvironment,
} from 'vite'

const server = await createServer({
server: { middlewareMode: true },
appType: 'custom',
environments: {
custom: {
dev: {
createEnvironment(name, config) {
return createFetchableDevEnvironment(name, config, {
handleRequest(request: Request): Promise<Response> | Response {
// 处理请求并返回响应
},
})
},
},
},
},
})

// 现在,任何使用环境 API 的人都可以调用 `dispatchFetch`
if (isFetchableDevEnvironment(server.environments.custom)) {
const response: Response = await server.environments.custom.dispatchFetch(
new Request('/request-to-handle'),
)
}
```

:::warning
Vite 会检查 `dispatchFetch` 方法的输入和输出:请求必须是全局 `Request` 类的一个实例,而响应必须是全局 `Response` 类的一个实例。如果不满足这些条件,Vite 将会抛出一个 `TypeError`。

尽管 `FetchableDevEnvironment` 是作为一个类来实现的,但请注意,Vite 团队可能会随时更改它,因为其被视为实现细节部分。
:::

## 默认 `RunnableDevEnvironment` {#default-runnabledevenvironment}

假设我们有一个配置为中间件模式的 Vite 服务器,如 [SSR 设置指南](/guide/ssr#setting-up-the-dev-server) 所述,我们可以使用环境 API 来实现 SSR 中间件。请记住,它不必命名为 `ssr`,因此在本例中我们将其命名为 `server`。错误处理部分已省略。
假设已按照[SSR 设置指南](/guide/ssr#setting-up-the-dev-server)中描述的方式配置了 Vite 服务器的中间件模式,现在我们使用环境 API 实现 SSR 中间件。请注意,它不必命名为 `ssr`,因此在本示例中我们将它命名为 `server`。错误处理已省略。

```js
import fs from 'node:fs'
Expand Down Expand Up @@ -142,42 +103,62 @@ app.use('*', async (req, res, next) => {
})
```

## 运行时无关的 SSR {#runtime-agnostic-ssr}

由于 `RunnableDevEnvironment` 只能用于在与 Vite 服务器相同的运行时中运行代码,它需要一个可以运行 Vite 服务器的运行时(即与 Node.js 兼容的运行时)。这意味着您需要使用原始的 `DevEnvironment` 来使其对运行时无关。
### `FetchableDevEnvironment`

:::info `FetchableDevEnvironment` 提议
:::info

最初的提议是在 `DevEnvironment` 类上有一个 `run` 方法,该方法将允许消费者通过使用 `transport` 选项在运行器端调用一个导入。在我们的测试中,我们发现 API 还不够通用,以至于我们暂时不开始推荐它。目前,我们正在寻求对 [`FetchableDevEnvironment` 提议](https://github.com/vitejs/vite/discussions/18191) 的反馈
我们正在征集对[`FetchableDevEnvironment`提案](https://github.com/vitejs/vite/discussions/18191)的反馈意见

:::

`RunnableDevEnvironment` 有一个 `runner.import` 函数,返回模块的值。但是这个函数在原始的 `DevEnvironment` 中不可用,并且需要将使用 Vite 的 API 和用户模块的代码解耦
`FetchableDevEnvironment` 是一种可以通过 [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch) 接口与运行时进行通信的环境。由于 `RunnableDevEnvironment` 仅能在有限的运行时环境中实现,我们建议使用 `FetchableDevEnvironment` 替代 `RunnableDevEnvironment`

例如,下面的例子中,使用 Vite API 的代码使用了用户模块的值
该环境通过`handleRequest`方法提供了一种标准化的请求处理方式

```ts
// 使用 Vite API 的代码
import { createServer } from 'vite'

const server = createServer()
const ssrEnvironment = server.environment.ssr
const input = {}
import {
createServer,
createFetchableDevEnvironment,
isFetchableDevEnvironment,
} from 'vite'

const { createHandler } = await ssrEnvironment.runner.import('./entrypoint.js')
const handler = createHandler(input)
const response = handler(new Request('/'))
const server = await createServer({
server: { middlewareMode: true },
appType: 'custom',
environments: {
custom: {
dev: {
createEnvironment(name, config) {
return createFetchableDevEnvironment(name, config, {
handleRequest(request: Request): Promise<Response> | Response {
// handle Request and return a Response
},
})
},
},
},
},
})

// -------------------------------------
// ./entrypoint.js
export function createHandler(input) {
return function handler(req) {
return new Response('hello')
}
// Any consumer of the environment API can now call `dispatchFetch`
if (isFetchableDevEnvironment(server.environments.custom)) {
const response: Response = await server.environments.custom.dispatchFetch(
new Request('/request-to-handle'),
)
}
```

如果你的代码可以在与用户模块相同的运行时中运行(即,它不依赖于 Node.js 特定的 API),你可以使用虚拟模块。这种方法避免了从使用 Vite API 的代码中获取值的需求。
:::warning
Vite 验证 `dispatchFetch` 方法的输入和输出:请求必须是全局 `Request` 类的实例,响应必须是全局 `Response` 类的实例。如果不符合此条件,Vite 将抛出 `TypeError` 异常。

请注意,尽管 `FetchableDevEnvironment` 作为类实现,但 Vite 团队将其视为实现细节,可能随时发生变化。
:::

### raw `DevEnvironment`

如果环境未实现 `RunnableDevEnvironment` 或 `FetchableDevEnvironment` 接口,您需要手动设置通信。

如果您的代码可以在与用户模块相同的运行时环境中运行(即不依赖于 Node.js 特定的 API),您可以使用虚拟模块。这种方法消除了通过 Vite 的 API 从代码中访问值的必要性。

```ts
// 使用 Vite API 的代码
Expand All @@ -198,8 +179,6 @@ const input = {}
// 使用每个环境工厂暴露的函数来运行代码
// 检查每个环境工厂提供了什么
if (ssrEnvironment instanceof RunnableDevEnvironment) {
ssrEnvironment.runner.import('virtual:entrypoint')
} else if (ssrEnvironment instanceof CustomDevEnvironment) {
ssrEnvironment.runEntrypoint('virtual:entrypoint')
} else {
throw new Error(`Unsupported runtime for ${ssrEnvironment.name}`)
Expand Down
2 changes: 2 additions & 0 deletions guide/api-environment-runtimes.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ function createWorkerdDevEnvironment(
}
```

[`DevEnvironment` 具有多个通信级别](/guide/api-environment-frameworks#devenvironment-communication-levels)。为了便于框架编写与运行时无关的代码,我们建议实现尽可能灵活的通信级别。

## `ModuleRunner`

一个模块运行器在目标运行时中实例化。下一节中的所有 API 都从 `vite/module-runner` 导入,除非另有说明。这个导出入口文件尽可能保持轻量,仅导出创建模块运行器所需的最小部分。
Expand Down
2 changes: 1 addition & 1 deletion guide/rolldown.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export default {
rollupOptions: {
output: {
advancedChunks: {
groups: [{ name: 'vendor', test: /\/react(?:-dom)?// }]
groups: [{ name: 'vendor', test: /\/react(?:-dom)?/ }]
}
}
}
Expand Down