跳轉到內容

後端整合

注意

如果你希望使用傳統的後端(例如 Rails、Laravel)來渲染 HTML,但使用 Vite 來提供資源服務,請檢視 Awesome Vite 中列出的現有整合。

如果你需要自定義整合,可以按照本指南中的步驟進行手動配置。

  1. 在你的 Vite 配置檔案中,配置入口點並啟用構建清單(build manifest)

    vite.config.js
    js
    export default 
    defineConfig
    ({
    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,
    rollupOptions
    : {
    // overwrite default .html entry
    input
    : '/path/to/main.js',
    }, }, })

    如果你沒有停用 module preload polyfill,你還需要在入口檔案中匯入該 polyfill

    js
    // add the beginning of your app entry
    import 'vite/modulepreload-polyfill'
  2. 在開發過程中,請在伺服器的 HTML 模板中注入以下內容(將 https://:5173 替換為 Vite 執行的本地 URL)

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

    為了正確提供資源服務,你有兩個選擇

    • 確保伺服器配置為將靜態資源請求代理到 Vite 伺服器
    • 設定 server.origin,以便生成的資源 URL 將使用後端伺服器 URL 解析,而不是使用相對路徑

    這對於圖片等資源的正常載入是必需的。

    注意:如果你在使用帶有 @vitejs/plugin-react 的 React,你還需要在上述指令碼之前新增此內容,因為該外掛無法修改你所提供的 HTML(將 https://:5173 替換為 Vite 執行的本地 URL)

    html
    <script type="module">
      import RefreshRuntime from 'https://: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 檔案。清單檔案的示例如下

    .vite/manifest.json
    json
    {
      "_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 建置工具foo 建置工具views/foo.js(entry)shared 建置工具_shared-B7PI925R.js(common chunk)foo->shared 建置工具importsfoocss 建置工具foo.cssfoo->foocss 建置工具cssbar 建置工具views/bar.js(entry)bar->shared 建置工具importsbaz 建置工具baz.js(dynamic import)bar->baz 建置工具dynamicImportssharedcss 建置工具shared.cssshared->sharedcss 建置工具csslogo 建置工具logo.svg(asset)
    manifest 建置工具foo 建置工具views/foo.js(entry)shared 建置工具_shared-B7PI925R.js(common chunk)foo->shared 建置工具importsfoocss 建置工具foo.cssfoo->foocss 建置工具cssbar 建置工具views/bar.js(entry)bar->shared 建置工具importsbaz 建置工具baz.js(dynamic import)bar->baz 建置工具dynamicImportssharedcss 建置工具shared.cssshared->sharedcss 建置工具csslogo 建置工具logo.svg(asset)

    清單具有 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[]
    }

    清單中的每個條目代表以下內容之一

    • 入口 chunk(Entry chunks):由 build.rollupOptions.input 中指定的檔案生成。這些 chunk 的 isEntry: true,其鍵是專案根目錄的相對源路徑。
    • 動態入口 chunk(Dynamic entry chunks):由動態匯入生成。這些 chunk 的 isDynamicEntry: true,其鍵是專案根目錄的相對源路徑。
    • 非入口 chunk(Non-entry chunks):其鍵是生成檔案的基本名稱,並帶有 _ 字首。
    • 資源 chunk(Asset chunks):由匯入的資源(如圖片、字型)生成。其鍵是專案根目錄的相對源路徑。
    • CSS 檔案:當 build.cssCodeSplitfalse 時,會生成一個帶有鍵 style.css 的單個 CSS 檔案。當 build.cssCodeSplit 不為 false 時,鍵的生成方式類似於 JS chunk(即入口 chunk 不會帶有 _ 字首,而非入口 chunk 則會帶有 _ 字首)。

    JS chunk(除資源或 CSS 以外的 chunk)將包含其靜態和動態匯入的資訊(兩者均為對映到清單中相應 chunk 的鍵)。如果 chunk 有對應的 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 }}" />

    具體來說,生成 HTML 的後端在給定清單檔案和入口點時,應包含以下標籤。注意,為了獲得最佳效能,建議遵循此順序

    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 開始遞迴遍歷匯入。

    根據上述示例清單,對於入口點 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>
    <!-- optional -->
    <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>
    <!-- optional -->
    <link rel="modulepreload" href="assets/shared-B7PI925R.js" />
    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])
    }