提供静态资源 Serve Static Assets

提供静态资产,例如 HTML、图像、CSS、JavaScript 等。

如果您使用 unjs/listhen,您只需在项目根目录中创建一个 public 目录并将静态资产放入其中。它们将自动提供。

用法

要提供静态目录,您可以使用 serveStatic 实用程序。

ts
import { createApp, defineEventHandler, serveStatic } from 'h3'

export const app = createApp()

app.use(
  defineEventHandler((event) => {
    return serveStatic(event, {
      getContents: (id) => {
        return undefined
      },
      getMeta: (id) => {
        return undefined
      },
    })
  }),
)

这还不能提供任何文件。您需要实现 getContentsgetMeta 方法。

  • getContents 用于读取文件的内容。它应该返回一个解析为文件内容的 Promise,如果文件不存在则返回 undefined
  • getMeta 用于获取文件的元数据。它应该返回一个解析为文件元数据的 Promise,如果文件不存在则返回 undefined

它们是分开的,以允许 h3 响应 HEAD 请求而不读取文件的内容并使用 Last-Modified 标头。

读取文件

现在,在 public 目录中创建一个带有简单消息的 index.html 文件,然后打开浏览器访问 http://localhost:3000。您应该会看到该消息。

public 的使用是一种惯例,但您可以使用任何您想要的目录名称。

如果您正在使用 unjs/listhen 并想尝试此示例,请创建一个名称不同于 public 的目录,因为它是 listhen 使用的默认目录。

然后,我们可以创建 getContentsgetMeta 方法:

ts
import { readFile, stat } from 'node:fs/promises'
import { createApp, defineEventHandler, serveStatic } from 'h3'
import { join } from 'pathe'

export const app = createApp()

const publicDir = 'assets'

app.use(
  defineEventHandler((event) => {
    return serveStatic(event, {
      getContents: id => readFile(join(publicDir, id)),
      getMeta: async (id) => {
        const stats = await stat(join(publicDir, id)).catch(() => {})

        if (!stats || !stats.isFile()) {
          return
        }

        return {
          size: stats.size,
          mtime: stats.mtimeMs,
        }
      },
    })
  }),
)

getContents 读取文件并返回其内容,非常简单。getMeta 使用 fs.stat 获取文件元数据。如果文件不存在或不是文件,则返回 undefined。否则,返回文件大小和上次修改时间。

如果文件自上次请求以来没有被修改,则使用文件大小和上次修改时间创建 etag 以发送 304 Not Modified 响应。如果文件没有更改,这可以避免多次发送相同的文件。

解析资产

如果路径与文件不匹配,h3 将尝试将 index.html 添加到路径并重试。如果仍然不匹配,它将返回 404 错误。

您可以通过将 indexNames 选项传递给 serveStatic 来更改此行为:

ts
import { createApp, serveStatic } from 'h3'

const app = createApp()

app.use(
  serveStatic({
    indexNames: ['/app.html', '/index.html'],
  }),
)

使用此选项,h3 将首先尝试匹配 <path>/app.html,然后是 <path>/index.html,最后返回 404 错误。

不要忘记 h3 开头的 / 将路径与索引名称连接起来。例如,/index.html 将与 /hello 连接起来形成 hello/index.html