章節連結
Nuxt4 預渲染時,可以搭配 api 跟後端拿資料,進而生成動態路由。這邊筆記下實作方法。

內容
基本上都是在 hook ( nitro:config ) 來進行設定。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
import { writeFileSync, readdirSync, statSync } from 'node:fs' import { resolve } from 'node:path' import { $fetch } from 'ofetch' import { defineNuxtConfig } from 'nuxt/config' export default defineNuxtConfig({ hooks: { async 'nitro:config'(config) { // 僅在生產環境構建 (SSG) 時執行 if (process.env.NODE_ENV !== 'production') return const baseUrl = process.env.NUXT_API_BASE_URL || '' if (!baseUrl) { console.warn('[Build] 警告:未設定 NUXT_API_BASE_URL,將跳過動態路由生成。') return } try { console.log(`[Build] 正在從 ${baseUrl} 獲取客戶名單...`) // 1. 獲取遠端資料 // 注意:根據您的原始碼,這裡處理了字串與物件的相容性 const rawRes = await $fetch<any>(`${baseUrl}/system/get_client`, { method: 'POST', body: {}, }) const res: Record<string, string>[] = typeof rawRes === 'string' ? JSON.parse(rawRes) : rawRes // 2. 處理資料映射 (Mapping) // 將 Array of Objects 扁平化並排序,建立 client 到品牌名稱的映射表 const clientMapping = res .flatMap((item) => Object.entries(item)) .sort(([keyA], [keyB]) => keyA.localeCompare(keyB)) const clientBrandMapping = Object.fromEntries(clientMapping) const clientSlugs = Object.keys(clientBrandMapping) // 3. 儲存映射文件供 Runtime 使用 (例如 SEO Middleware 或頁面組件) const mappingFilePath = resolve(process.cwd(), 'public/client-brand-mapping.json') writeFileSync(mappingFilePath, JSON.stringify(clientBrandMapping, null, 2), 'utf-8') console.log(`[Build] 成功寫入映射文件:${mappingFilePath}`) // 4. 自動偵測 pages 下的動態路由檔案 // 假設路徑為 app/pages/clients/[client]/,偵測該層級下的所有 .vue 檔案 const dynamicPagesDir = resolve(process.cwd(), 'app/pages/clients/[client]') const pageFiles = readdirSync(dynamicPagesDir) .filter((file) => { const filePath = resolve(dynamicPagesDir, file) return statSync(filePath).isFile() && file.endsWith('.vue') }) .map((file) => file.replace('.vue', '')) // 移除副檔名取得路由名稱 // 5. 生成所有組合的動態路由 const routes = clientSlugs.flatMap((client) => pageFiles.map((page) => `/clients/${client}/${page}/`) ) // 6. 注入 Nitro 預渲染排程 config.prerender = config.prerender || {} config.prerender.routes = config.prerender.routes || [] config.prerender.routes.push(...routes) console.log('--- SSG Generation Summary ---') console.log(`[Build] 目標頁面:${pageFiles.join(', ')}`) console.log(`[Build] 客戶總數:${clientSlugs.length}`) console.log(`[Build] 總共注入 ${routes.length} 個預渲染路由`) console.log('------------------------------') } catch (error) { console.error('[Build] 預渲染流程失敗:', error) if (process.env.CI) { throw new Error('SSG 失敗,停止 CI/CD 部署。') } } }, } }) |
按讚加入粉絲團
