Vite 后端集成

注意

如果你希望使用传统后端(例如 Rails 或 Laravel)提供 HTML,同时使用 Vite 提供资源,可以先查看 Awesome Vite 中列出的现成集成方案。

如果需要自定义集成,可以按以下指南手动配置。

  1. 在 Vite 配置中设置入口,并启用构建清单(manifest):

    vite.config.jsjs

    export default defineConfig({
      // overwrite default .html entry
      input: '/path/to/main.js',
      server: {
        cors: {
          // the origin you will be accessing via browser
          origin: 'http://my-backend.example.com',
        },
      },
      build: {
        // generate .vite/manifest.json in outDir
        manifest: true,
      },
    })

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

    js

    // add the beginning of your app entry
    import 'vite/modulepreload-polyfill'
  2. 开发期间,在服务器的 HTML 模板中注入以下代码;将 http://localhost:5173 替换为 Vite 实际运行的本地地址:

    html

    <!-- if development -->
    <script type="module" src="http://localhost:5173/@vite/client"></script>
    <script type="module" src="http://localhost:5173/main.js"></script>

    此外,为了正确提供资源,可以选择以下两种做法之一:

    • 将服务器的静态资源请求代理到 Vite。
    • 设置 server.origin,使生成的资源 URL 使用后端服务器的地址,而不是相对路径。

    只有完成这项配置,图片等资源才能正确加载。

    如果将 React 与 @vitejs/plugin-react 一起使用,还需要在前面提到的脚本之前加入下面的代码,因为该插件无法修改你正在提供的 HTML。将 http://localhost:5173 替换为 Vite 实际运行的本地地址。

    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 文件示例如下:

    .vite/manifest.jsonjson

    {
      "_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 将源文件映射到构建输出及其依赖。

    manifest 的结构为 Record<name, chunk>。其中每个 chunk 都符合 ManifestChunk 接口:

    ts

    interface ManifestChunk {
      /**
       * The input file name of this chunk / asset if known
       */
      src?: string
      /**
       * The output file name of this chunk / asset
       */
      file: string
      /**
       * The list of CSS files imported by this chunk
       */
      css?: string[]
      /**
       * The list of asset files imported by this chunk, excluding CSS files
       */
      assets?: string[]
      /**
       * Whether this chunk or asset is an entry point
       */
      isEntry?: boolean
      /**
       * The name of this chunk / asset if known
       */
      name?: string
      /**
       * Whether this chunk is a dynamic entry point
       *
       * This field is only present in JS chunks.
       */
      isDynamicEntry?: boolean
      /**
       * The list of statically imported chunks by this chunk
       *
       * The values are the keys of the manifest. This field is only present in JS chunks.
       */
      imports?: string[]
      /**
       * The list of dynamically imported chunks by this chunk
       *
       * The values are the keys of the manifest. This field is only present in JS chunks.
       */
      dynamicImports?: string[]
    }

    manifest 中的 chunk 类型包括:

    • 入口 chunk 在 build.rolldownOptions.input 中指定,带有 isEntry: true 标志;其键是相对于项目根目录的源文件路径。
    • 动态导入的入口 chunk 带有 isDynamicEntry: true 标志;其键也是相对于项目根目录的源文件路径。
    • 非入口 chunk 的键由生成文件的基础文件名加上 _ 前缀构成。
    • 图片、字体等资源 chunk 的键,是相对于项目根目录的源文件路径。
    • 当 build.cssCodeSplit 为 false 时,会生成一个键为 style.css 的 CSS 文件。如果 build.cssCodeSplit 为 true,则 CSS chunk 的生成方式与 JavaScript chunk 类似:入口 chunk 的键使用其源文件路径,非入口 chunk 的键使用 _ 前缀和生成文件的基础文件名。

    JavaScript chunk 包含 imports 和 dynamicImports 字段,用于列出其静态导入和动态导入的 chunk(这些值是 manifest 中的键);css 和 assets 字段则用于列出对应的 CSS 与资源文件。

  4. 你可以使用这个文件,通过带哈希的文件名渲染链接或预加载指令。

    下面是渲染正确链接的 HTML 模板示例。此处的语法仅用于说明,应替换成后端模板语言支持的写法。importedChunks 函数仅用于演示,Vite 不提供该函数。

    html

    <!-- if production -->
    
    <!-- for cssFile of manifest[name].css -->
    <link rel="stylesheet" href="/{{ cssFile }}" />
    
    <!-- for chunk of importedChunks(manifest, name) -->
    <!-- for cssFile of chunk.css -->
    <link rel="stylesheet" href="/{{ cssFile }}" />
    
    <script type="module" src="/{{ manifest[name].file }}"></script>
    
    <!-- for chunk of importedChunks(manifest, name) -->
    <link rel="modulepreload" href="/{{ chunk.file }}" />

    建议在后端按照以下顺序渲染标签:

    1. 为入口 chunk 的 css 列表中的每个文件渲染一个 <link rel="stylesheet"> 标签(如果该列表存在)。
    2. 沿入口 chunk 的 imports 列表递归访问所有导入的 chunk,并为各 chunk 的 css 列表中的每个文件渲染一个 <link rel="stylesheet"> 标签(如果该列表存在)。
    3. 为入口 chunk 的 file 字段渲染标签:如果它是 JavaScript 文件,渲染 <script type="module">;如果是 CSS 文件,渲染 <link rel="stylesheet">。
    4. 可选:从入口 chunk 的 imports 列表出发递归访问导入的 chunk,为每个 JavaScript chunk 的 file 文件渲染一个 <link rel="modulepreload"> 标签。

    接着,为 views/foo.js 入口渲染生产环境中的 HTML:

    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>
    <!-- optional -->
    <link rel="modulepreload" href="assets/shared-B7PI925R.js" />

    为 views/bar.js 入口渲染生产环境中的 HTML:

    html

    <link rel="stylesheet" href="assets/shared-ChJ_j-JJ.css" />
    <script type="module" src="assets/bar-gkvgaI9m.js"></script>
    <!-- optional -->
    <link rel="modulepreload" href="assets/shared-B7PI925R.js" />
    Pseudo implementation of importedChunks

    下面是 importedChunks 的 TypeScript 伪代码。应根据后端编程语言或模板语言调整它:

    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])
    }

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

    如果启用了实验性的 build.chunkImportMap,你还需要手动在 HTML 中注入导入映射。

    导入映射会输出到输出目录中的 importmap.json。应读取它的内容,在任何 <script type="module"> 标签或 <link rel="modulepreload"> 标签之前注入 <script type="importmap"> 标签。


来源:Vite 官方文档。

© 2019 至今 VoidZero Inc. 和 Vite 贡献者。来源页面版本标识:39ddf7cc。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容