知海

后端集成

vite-main使用指南

后端集成

:::tip 提示
如果你想使用传统后端(例如 Rails、Laravel)来提供 HTML 服务,但使用 Vite 来提供静态资源,请查看 Awesome Vite 中列出的现有集成方案。

如果你需要自定义集成,可以按照本指南中的步骤手动配置。
:::

  1. 在你的 Vite 配置中,配置入口并启用构建 manifest:

    js twoslash [vite.config.js] 复制代码
    import { defineConfig } from 'vite'
    // ---cut---
    export default defineConfig({
      // 覆盖默认的 .html 入口
      input: '/path/to/main.js',
      server: {
        cors: {
          // 你通过浏览器访问的来源
          origin: 'http://my-backend.example.com',
        },
      },
      build: {
        // 在 outDir 中生成 .vite/manifest.json
        manifest: true,
      },
    })

    如果你还没有禁用 模块预加载 polyfill,你还需要在你的入口中导入该 polyfill:

    js 复制代码
    // 在你的应用入口开头添加
    import 'vite/modulepreload-polyfill'
  2. 在开发环境中,将以下内容注入到你的服务器 HTML 模板中(将 http://localhost:5173 替换为 Vite 运行时的本地 URL):

    html 复制代码
    <!-- 如果是开发环境 -->
    <script type="module" src="http://localhost:5173/@vite/client"></script>
    <script type="module" src="http://localhost:5173/main.js"></script>

    为了正确提供静态资源服务,你有两种选择:

    • 确保服务器配置了将静态资源请求代理到 Vite 服务器
    • 设置 server.origin,这样生成的资源 URL 将使用后端服务器 URL 而不是相对路径来解析

    这对于图片等资源的正常加载是必需的。

    请注意,如果你使用 React 和 @vitejs/plugin-react,你还需要在上述脚本之前添加以下内容,因为该插件无法修改你所提供的 HTML(将 http://localhost:5173 替换为 Vite 运行时的本地 URL):

    html 复制代码
    <script type="module">
      import RefreshRuntime from 'http://localhost:5173/@react-refresh'
      RefreshRuntime.injectIntoGlobalHook(window)
      window.$RefreshReg$ = () => {}
      window.$RefreshSig$ = () => (type) => type
      window.__vite_plugin_react_preamble_installed__ = true
    </script>
  3. 在生产环境中,运行 vite build 后,会在其他资源文件旁边生成一个 .vite/manifest.json 文件。以下是一个 manifest 文件的示例:

    json [.vite/manifest.json] style:max-height:400px 复制代码
    {
      "_shared-B7PI925R.js": {
        "file": "assets/shared-B7PI925R.js",
        "name": "shared",
        "css": ["assets/shared-ChJ_j-JJ.css"]
      },
      "_shared-ChJ_j-JJ.css": {
        "file": "assets/shared-ChJ_j-JJ.css",
        "src": "_shared-ChJ_j-JJ.css"
      },
      "logo.svg": {
        "file": "assets/logo-BuPIv-2h.svg",
        "src": "logo.svg"
      },
      "baz.js": {
        "file": "assets/baz-B2H3sXNv.js",
        "name": "baz",
        "src": "baz.js",
        "isDynamicEntry": true
      },
      "views/bar.js": {
        "file": "assets/bar-gkvgaI9m.js",
        "name": "bar",
        "src": "views/bar.js",
        "isEntry": true,
        "imports": ["_shared-B7PI925R.js"],
        "dynamicImports": ["baz.js"]
      },
      "views/foo.js": {
        "file": "assets/foo-BRBmoGS9.js",
        "name": "foo",
        "src": "views/foo.js",
        "isEntry": true,
        "imports": ["_shared-B7PI925R.js"],
        "css": ["assets/foo-5UjPuW-k.css"]
      }
    }

    Manifest 将源文件映射到它们的构建输出和依赖关系:

    graph TD foo["views/foo.js<br/>(入口)"] -->|imports| shared["_shared-B7PI925R.js<br/>(公共代码块)"] bar["views/bar.js<br/>(入口)"] -->|imports| shared bar -->|dynamicImports| baz["baz.js<br/>(动态导入)"] foo -->|css| foocss["foo.css<br/>(资源)"] shared -->|css| sharedcss["shared.css<br/>(资源)"]

    Manifest 具有 Record<name, chunk> 结构,其中每个 chunk 遵循 ManifestChunk 接口:

    ts style:max-height:400px 复制代码
    interface ManifestChunk {
      /**
       * 该 chunk / 资源的输入文件名(如果已知)
       */
      src?: string
      /**
       * 该 chunk / 资源的输出文件名
       */
      file: string
      /**
       * 该 chunk 导入的 CSS 文件列表
       */
      css?: string[]
      /**
       * 该 chunk 导入的资源文件列表(不包括 CSS 文件)
       */
      assets?: string[]
      /**
       * 该 chunk 或资源是否为入口点
       */
      isEntry?: boolean
      /**
       * 该 chunk / 资源的名称(如果已知)
       */
      name?: string
      /**
       * 该 chunk 是否为动态入口点
       *
       * 此字段仅存在于 JS chunk 中。
       */
      isDynamicEntry?: boolean
      /**
       * 该 chunk 静态导入的 chunk 列表
       *
       * 这些值是 manifest 的键。此字段仅存在于 JS chunk 中。
       */
      imports?: string[]
      /**
       * 该 chunk 动态导入的 chunk 列表
       *
       * 这些值是 manifest 的键。此字段仅存在于 JS chunk 中。
       */
      dynamicImports?: string[]
    }

    Manifest 中的每个条目代表以下之一:

    • 入口 chunk:由 build.rolldownOptions.input 中指定的文件生成。这些 chunk 具有 isEntry: true,其键是相对于项目根目录的 src 路径。
    • 动态入口 chunk:由动态导入生成。这些 chunk 具有 isDynamicEntry: true,其键是相对于项目根目录的 src 路径。
    • 非入口 chunk:其键是生成文件的基础名称,并以 _ 作为前缀。
    • 资源 chunk:由导入的资源(如图片、字体)生成。其键是相对于项目根目录的 src 路径。
    • CSS 文件:当 build.cssCodeSplitfalse 时,会生成一个键为 style.css 的单独 CSS 文件。当 build.cssCodeSplit 不为 false 时,键的生成方式与 JS chunk 类似(即入口 chunk 不会有 _ 前缀,非入口 chunk 会有 _ 前缀)。

    JS chunk(资源或 CSS 之外的 chunk)将包含其静态和动态导入的信息(两者都是映射到 manifest 中相应 chunk 的键)。Chunk 也会列出它们对应的 CSS 和资源文件(如果有的话)。

  4. 你可以使用此文件来渲染带有哈希文件名的链接或预加载指令。

    以下是一个渲染正确链接的 HTML 模板示例。这里的语法仅用于说明,请替换为你的服务器模板语言。importedChunks 函数仅用于演示,并非由 Vite 提供。

    html 复制代码
    <!-- 如果是生产环境 -->
    
    <!-- 对于 manifest[name].css 中的每个 cssFile -->
    <link rel="stylesheet" href="/{{ cssFile }}" />
    
    <!-- 对于 importedChunks(manifest, name) 中的每个 chunk -->
    <!-- 对于 chunk.css 中的每个 cssFile -->
    <link rel="stylesheet" href="/{{ cssFile }}" />
    
    <script type="module" src="/{{ manifest[name].file }}"></script>
    
    <!-- 对于 importedChunks(manifest, name) 中的每个 chunk -->
    <link rel="modulepreload" href="/{{ chunk.file }}" />

    具体来说,生成 HTML 的后端在给定 manifest 文件和入口点的情况下,应包含以下标签。请注意,建议按照以下顺序以获得最佳性能:

    1. 为入口点 chunk 的 css 列表中的每个文件添加一个 <link rel="stylesheet"> 标签(如果存在)
    2. 递归地遍历入口点 imports 列表中的所有 chunk,并为每个导入 chunk 的 css 列表中的每个 CSS 文件添加一个 <link rel="stylesheet"> 标签(如果存在)。
    3. 为入口点 chunk 的 file 键添加一个标签。对于 JavaScript 可以是 <script type="module">,对于 CSS 可以是 <link rel="stylesheet">
    4. 可选地,为每个导入的 JavaScript chunk 的 file 添加一个 <link rel="modulepreload"> 标签,同样从入口点 chunk 开始递归地遍历 imports。

    根据上述 manifest 示例,对于入口点 views/foo.js,在生产环境中应包含以下标签:

    html 复制代码
    <link rel="stylesheet" href="assets/foo-5UjPuW-k.css" />
    <link rel="stylesheet" href="assets/shared-ChJ_j-JJ.css" />
    <script type="module" src="assets/foo-BRBmoGS9.js"></script>
    <!-- 可选 -->
    <link rel="modulepreload" href="assets/shared-B7PI925R.js" />

    而对于入口点 views/bar.js,则应包含以下内容:

    html 复制代码
    <link rel="stylesheet" href="assets/shared-ChJ_j-JJ.css" />
    <script type="module" src="assets/bar-gkvgaI9m.js"></script>
    <!-- 可选 -->
    <link rel="modulepreload" href="assets/shared-B7PI925R.js" />

    ::: details importedChunks 的伪实现
    一个用 TypeScript 编写的 importedChunks 示例伪实现(这需要根据你的编程语言和模板语言进行调整):

    ts 复制代码
    import type { Manifest, ManifestChunk } from 'vite'
    
    export default function importedChunks(
      manifest: Manifest,
      name: string,
    ): ManifestChunk[] {
      const seen = new Set<string>()
    
      function getImportedChunks(chunk: ManifestChunk): ManifestChunk[] {
        const chunks: ManifestChunk[] = []
        for (const file of chunk.imports ?? []) {
          const importee = manifest[file]
          if (seen.has(file)) {
            continue
          }
          seen.add(file)
    
          chunks.push(...getImportedChunks(importee))
          chunks.push(importee)
        }
    
        return chunks
      }
    
      return getImportedChunks(manifest[name])
    }

    info Chunk 导入映射支持(实验性)

    如果你正在使用实验性的 build.chunkImportMap 选项,你还需要将导入映射注入到 HTML 中。

    导入映射会输出到输出目录中的 importmap.json 文件。请确保在任何 <script type="module"> 标签或 <link rel="modulepreload"> 标签之前注入 <script type="importmap"> 标签。
    :::

帮助我们改进文档

发现翻译问题或内容错误?请告诉我们。