飯店集團網站:使用 Next.js 的多物業架構
您的區域總監在下午 4 點發來郵件:新收購的查爾斯頓精品飯店在八週內上線,品牌標準必須遵守,預訂引擎已整合,沒有例外。您打開目前的多網站設置——十七個 WordPress 安裝,每個都有自己的外掛迷宮,每個在更新後都會以不同方式崩潰,每個都需要一個單獨的測試環境。您的開發團隊估計至少需要十二週。我親眼目睹過這個確切的情況在運營 8 個、25 個,甚至 47 個分店的飯店集團中摧毀啟動時間表。您今天做出的架構決策決定了您下一間分店是需要三天還是三個月才能上線。大多數集團選擇感覺安全的路徑——然後花兩年時間來理清它。這是 Next.js 方法,可以讓一個平台為每個分店提供支援,而不會產生混亂。
有更好的方法。一個正確架構的單一 Next.js 應用程式——可以從一個程式碼庫、一個部署管道和一個內容管理層為飯店集團的每個分店提供服務。每個分店都有自己的品牌、自己的內容、自己的網域。工程團隊可以恢復理智。
本文詳細說明了如何構建該系統。不是理論——我們在真實飯店集團項目中使用過的實際架構模式。
目錄
- 為什麼飯店集團需要統一平台
- 架構概述:一個程式碼庫,多個分店
- Next.js 中的多租戶模式
- 飯店集團的無頭 CMS 策略
- 共用元件與分店級自訂
- 預訂引擎整合
- 網域路由和分店解析
- 大規模效能
- 集中式管理儀表板
- 部署和 DevOps
- 實世界成本比較
- 常見問題

為什麼飯店集團需要統一平台
典型的飯店集團網站情況如下所示:分店 A 運行 2019 年的主題 WordPress。分店 B 在 Squarespace 上,因為總經理的侄子設置了它。分店 C 有一個自訂的 PHP 網站,沒有人想要接觸它。公司網站在完全不同的平台上。
每個分店更新都需要不同的工作流程。品牌一致性是天方夜譚。SEO 策略在數十個網域之間分散,沒有共享權限。當公司決定添加新的便利設施徽章或更新預訂小工具時,必須在 15 個不同的地方進行該更改。
成本不斷累積:
- 維護開銷:每個平台都需要自己的主機、安全補丁、外掛更新
- 品牌漂移:分店慢慢偏離品牌準則
- 開發人員上下文切換:您的團隊(或代理)需要跨多個平台的專業知識
- 緩慢的分店啟動:新收購需要數個月才能上線
- 分析碎片化:無法統一查看整個投資組合的效能
集中式多分店平台解決了所有這些問題。一個程式碼庫。一個部署。一個 CMS。透過配置提供每個分店的內容和品牌,而不是單獨的程式碼庫。
架構概述:一個程式碼庫,多個分店
以下是運行良好的高層次架構:
┌─────────────────────────────────────────────┐
│ CDN / Edge Network │
│ (Vercel, Cloudflare, Fastly) │
├─────────────────────────────────────────────┤
│ Next.js Application │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Property │ │ Property │ │ Property │ │
│ │ Resolver │ │ Theming │ │ Content │ │
│ │ Middleware│ │ Engine │ │ Fetcher │ │
│ └──────────┘ └──────────┘ └──────────┘ │
├─────────────────────────────────────────────┤
│ API Layer │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Headless │ │ Booking │ │ Media │ │
│ │ CMS │ │ Engine │ │ CDN │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────┘
Next.js 應用程式充當渲染層。中介軟體確定正在請求哪個分店(透過網域、子網域或路徑)。主題引擎應用特定於分店的樣式。內容提取器從無頭 CMS 中提取分店範圍的內容。
下游的所有內容——CMS、預訂引擎、媒體儲存——都會透過分店識別碼進行查詢。該識別碼是將整個系統綁定在一起的線程。
Next.js 中的多租戶模式
Next.js 中有三種主要的多租戶方法。各有各的權衡。
模式 1:基於子網域的路由
每個分店都有一個子網域:grandplaza.hotelgroup.com、seasideresort.hotelgroup.com。
Next.js 中介軟體會攔截請求、提取子網域並解析分店配置:
// middleware.ts
import { NextRequest, NextResponse } from 'next/server';
import { getPropertyByDomain } from '@/lib/properties';
export function middleware(request: NextRequest) {
const hostname = request.headers.get('host') || '';
const subdomain = hostname.split('.')[0];
const property = getPropertyByDomain(subdomain);
if (!property) {
return NextResponse.redirect(new URL('/not-found', request.url));
}
// Inject property context into headers for downstream use
const response = NextResponse.next();
response.headers.set('x-property-id', property.id);
response.headers.set('x-property-slug', property.slug);
return response;
}
優點:乾淨的 URL、輕鬆的分店隔離、對不需要單獨 TLD 的分店來說有利於 SEO。
缺點:萬用字元 SSL 憑證管理、每個分店的品牌獨立性較低。
模式 2:自訂網域對應
每個分店都有自己的網域:grandplazahotel.com、seasideresort.com。
這就是大多數飯店集團實際想要的。中介軟體邏輯類似,但您是對著網域查詢表進行匹配:
const DOMAIN_MAP: Record<string, string> = {
'grandplazahotel.com': 'grand-plaza',
'www.grandplazahotel.com': 'grand-plaza',
'seasideresort.com': 'seaside-resort',
'www.seasideresort.com': 'seaside-resort',
};
Vercel 原生支援每個項目的自訂網域,您可以在其 Pro 計劃上對應多達 50 個網域(2026 年的定價為 $20/月)。對於更大的投資組合,他們的企業計劃會移除該限制。
優點:完整的品牌獨立性、保留現有網域權益。
缺點:DNS 管理開銷、更複雜的 SSL 配置。
模式 3:基於路徑的路由
一個網域下的所有分店:hotelgroup.com/properties/grand-plaza、hotelgroup.com/properties/seaside-resort。
優點:最簡單的實施、對 SEO 的統一網域權限。
缺點:每個分店的品牌身份較低、URL 結構感覺像公司網站。
| 模式 | 品牌獨立性 | SEO 靈活性 | 實施複雜度 | 最適合 |
|---|---|---|---|---|
| 子網域 | 中等 | 中等 | 低 | 預算有限的集團 |
| 自訂網域 | 高 | 高 | 中等 | 已建立的品牌 |
| 基於路徑 | 低 | 高(統一) | 最低 | 新投資組合網站 |
我們與 Social Animal 合作的大多數飯店集團最終都選擇了自訂網域對應。分店在其網域中具有品牌權益,行銷團隊希望獲得獨立性。

飯店集團的無頭 CMS 策略
CMS 的選擇會決定這個架構的成敗。您需要一個在內容層級支援多租戶的系統——分店 A 的編輯無法意外修改分店 B 的內容,但公司管理員可以管理所有內容。
運行良好的 CMS 選項
Sanity 是我對飯店集團的首選。其文件級權限、自訂 Studio 配置和 GROQ 查詢語言使分店範圍的內容檢索變得輕而易舉。您可以構建一個具有每個分店工作區檢視的單一 Sanity Studio。定價從 Team 計劃的 $99/月開始(2026 年定價),並且可以很好地擴展到大型內容量。
Contentful 如果您已經在他們的生態系統中運行。他們的空間級隔離映射得很好到分店,不過成本可能會增加——Premium 計劃上的每個空間都會增加成本,對於企業規模的飯店集團需求,您需要支付 $2,500+/月。
Strapi(自託管)是預算選項。您需要使用自訂中介軟體和基於角色的存取控制自己建立多租戶層,但沒有每座位許可成本。
我們在 無頭 CMS 開發指南 中涵蓋了完整的 CMS 選擇過程。
飯店的內容建模
以下是跨分店運行的內容模型:
// Sanity schema example
export const property = defineType({
name: 'property',
title: 'Property',
type: 'document',
fields: [
defineField({ name: 'name', type: 'string' }),
defineField({ name: 'slug', type: 'slug' }),
defineField({ name: 'domain', type: 'string' }),
defineField({ name: 'brand', type: 'reference', to: [{ type: 'brand' }] }),
defineField({ name: 'location', type: 'geopoint' }),
defineField({ name: 'theme', type: 'propertyTheme' }),
defineField({ name: 'bookingEngineId', type: 'string' }),
],
});
export const room = defineType({
name: 'room',
title: 'Room Type',
type: 'document',
fields: [
defineField({ name: 'property', type: 'reference', to: [{ type: 'property' }] }),
defineField({ name: 'name', type: 'string' }),
defineField({ name: 'description', type: 'blockContent' }),
defineField({ name: 'maxOccupancy', type: 'number' }),
defineField({ name: 'amenities', type: 'array', of: [{ type: 'reference', to: [{ type: 'amenity' }] }] }),
defineField({ name: 'gallery', type: 'array', of: [{ type: 'image' }] }),
],
});
關鍵模式:每個內容文件都參考一個 property。查詢始終按分店進行篩選。編輯只看到他們分店的內容。公司管理員看到一切。
共用元件與分店級自訂
這是架構變得有趣的地方。您希望 80% 的元件跨分店共用,20% 允許每個分店自訂。
主題層
為每個分店建立一個主題配置,該配置輸入到您的元件系統中:
// types/theme.ts
export interface PropertyTheme {
colors: {
primary: string;
secondary: string;
accent: string;
background: string;
text: string;
};
typography: {
headingFont: string;
bodyFont: string;
};
logo: {
light: string;
dark: string;
};
borderRadius: 'none' | 'sm' | 'md' | 'lg';
heroStyle: 'fullbleed' | 'contained' | 'split';
}
2025 年發佈的 Tailwind CSS v4 透過其優先的 CSS 配置和原生主題函數支援使這變得容易得多。您可以在佈局層級設置 CSS 自訂屬性,並讓它們級聯通過每個元件:
// app/layout.tsx
export default async function PropertyLayout({ children }: { children: React.ReactNode }) {
const property = await getCurrentProperty();
const theme = property.theme;
return (
<html
style={{
'--color-primary': theme.colors.primary,
'--color-secondary': theme.colors.secondary,
'--font-heading': theme.typography.headingFont,
'--font-body': theme.typography.bodyFont,
} as React.CSSProperties}
>
<body className="font-body text-text bg-background">
{children}
</body>
</html>
);
}
元件組成
共用元件接受主題代幣,每個分店以不同方式渲染,無需分支邏輯:
// components/HeroSection.tsx
export function HeroSection({ property }: { property: Property }) {
const heroConfig = property.theme.heroStyle;
const variants = {
fullbleed: 'h-screen w-full',
contained: 'h-[70vh] max-w-7xl mx-auto rounded-2xl overflow-hidden',
split: 'grid grid-cols-2 h-[80vh]',
};
return (
<section className={variants[heroConfig]}>
{/* Shared hero content structure */}
</section>
);
}
預訂引擎整合
飯店網站的存在只有一個原因:推動預訂。預訂引擎整合需要非常穩定。
大多數飯店集團使用以下預訂引擎之一:SynXis(Sabre)、Pegasus、Bookassist、SiteMinder 或專有的中央預訂系統。整合模式幾乎總是相同的:傳遞分店識別碼、日期範圍和客人數量以獲得可用性。
// lib/booking.ts
export async function checkAvailability({
propertyCode,
checkIn,
checkOut,
adults,
children,
}: BookingQuery) {
const response = await fetch(`${BOOKING_ENGINE_URL}/availability`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${BOOKING_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
hotel_code: propertyCode,
arrival: checkIn,
departure: checkOut,
guests: { adults, children },
}),
});
return response.json();
}
對於預訂小工具本身,您有兩個選項:
- 嵌入式 iframe:預訂引擎提供您嵌入的小工具。最少的工作、最少的控制。
- API 驅動的自訂 UI:您建立搜索和結果 UI、直接呼叫預訂 API,並僅在付款時移交給預訂引擎。更多工作、更好的用戶體驗。
選項 2 是 Next.js 架構真正閃耀的地方。您可以構建一個精美的、快速的、符合品牌的預訂體驗,感覺像是每個分店的原生體驗。Server Components 可以預先提取可用性數據。預訂流程保留在您的網域上,這有利於轉換追蹤和 SEO。
網域路由和分店解析
分店解析流程需要快速。真的很快。它在每個單一請求上運行。
以下是在生產環境中運行的模式:
- Edge 中介軟體解析網域 → 分店 slug(記憶體內查詢,亞毫秒級)
- 分店配置使用 Vercel Edge Config 或 Cloudflare KV 在邊緣進行快取
- 完整分店數據(主題、導航、頁腳內容)透過 ISR 在每個建置時期提取一次,或在請求時快取提取
// lib/property-resolver.ts
import { get } from '@vercel/edge-config';
export async function resolveProperty(hostname: string): Promise<PropertyConfig | null> {
// First: check edge config (sub-5ms)
const domainMap = await get<Record<string, string>>('domain-map');
const propertySlug = domainMap?.[hostname];
if (!propertySlug) return null;
// Second: get full property config (cached)
const propertyConfig = await get<PropertyConfig>(`property:${propertySlug}`);
return propertyConfig;
}
Vercel Edge Config 非常適合此目的——它是一個全域分散的鍵值存儲,讀取延遲低於 1 毫秒。在 Pro 計劃上,它對高達 512KB 的數據成本 $0,這對於分店查詢表來說綽綽有餘。
大規模效能
飯店網站具有特定的效能特徵,這很重要:
- 圖像豐富頁面:房間畫廊、分店相片、目的地圖像
- 季節性流量尖峰:假期、會議季節、當地活動
- 全球觀眾:國際旅客從各地瀏覽
- 轉換關鍵:每 100 毫秒的負載時間都會損失預訂
靜態生成策略
為分店頁面使用增量靜態重新生成 (ISR)。飯店內容不會每分鐘變化——60 秒的重新驗證週期通常很好:
// app/[propertySlug]/page.tsx
export async function generateStaticParams() {
const properties = await getAllProperties();
return properties.map((p) => ({ propertySlug: p.slug }));
}
export const revalidate = 60;
對於 30 個分店的集團,每個分店約 20 頁,您正在預先生成約 600 頁。Next.js 毫不費力地處理這個。建置時間保持在 5 分鐘以內。
圖像最佳化
Next.js Image 元件搭配遠端加載程式處理每個分店圖像最佳化。如果您使用 Sanity,他們的圖像 CDN 具有自動格式轉換和調整大小,效果極好。Cloudinary 是另一個堅實的選擇,Plus 計劃為 $89/月。
典型的飯店分店頁面應以以下為目標:
- LCP 在 4G 連線上低於 2.5 秒
- CLS 為 0(無圖像負載導致的佈局移位)
- 初始負載時的總頁面重量低於 1.5MB
集中式管理儀表板
除了 CMS 之外,飯店集團還需要操作儀表板。這是您建立自訂工具的地方:
- 分店概述:每個分店網站的狀態(上線、測試、維護)
- 內容新鮮度:哪些分店尚未更新其季節性內容
- 效能監控:每個分店的核心 Web 生命值
- 分析匯總:跨所有分店的預訂漏斗指標
我們通常將其構建為一個單獨的 Next.js 應用程式(通常使用 App Router 的伺服器端功能),該應用程式連接到相同的數據來源。管理儀表板是一個內部工具——它不需要華麗,但它需要有效。
部署和 DevOps
一個程式碼庫意味著一個 CI/CD 管道。以下是部署流程:
- 程式碼變更:PR → 審查 → 合併到 main
- 建置:Next.js 跨所有分店建置所有靜態頁面
- 部署:Vercel(或類似)部署到邊緣網絡
- DNS:每個分店網域指向部署
內容變更不需要部署。無頭 CMS 透過 webhook 觸發 ISR 重新驗證:
// app/api/revalidate/route.ts
export async function POST(request: Request) {
const body = await request.json();
const { propertySlug, contentType } = body;
// Revalidate specific paths for the changed property
revalidatePath(`/${propertySlug}`);
if (contentType === 'room') {
revalidatePath(`/${propertySlug}/rooms`);
}
return Response.json({ revalidated: true });
}
實世界成本比較
讓我們比較一個 20 間分店飯店集團的實際成本:
| 成本類別 | 單獨網站(WordPress) | 統一 Next.js 平台 |
|---|---|---|
| 主機(每月) | $2,000-4,000(20 × 受管 WP) | $150-400(Vercel Pro/Team) |
| CMS 許可 | $0-600(每個網站的外掛) | $99-300(Sanity/Contentful) |
| SSL 憑證 | $0-400(如果不使用 Let's Encrypt) | $0(自動配置) |
| 維護(年度) | $40,000-80,000(更新、安全) | $10,000-20,000 |
| 新分店啟動 | $5,000-15,000 每個網站 | $500-2,000(內容 + 配置) |
| 年度總計(預估) | $75,000-150,000 | $15,000-35,000 |
數字根本沒有可比性。這甚至不包括開發人員體驗的改進——擁有一個程式碼庫意味著您的團隊實際上理解該系統。不再有「該分店運行的是哪個 WordPress 版本?」
對於正在考慮此方法的飯店集團,我們已概述了我們的 Next.js 開發功能,您可以查看 我們的定價結構 以獲取更詳細的估計。
常見問題
將飯店集團遷移到統一 Next.js 平台需要多長時間? 對於 10-20 間分店的集團,預計從啟動到全面上線需要 3-5 個月。第一個分店花費最長時間(8-10 週),因為您正在構建平台。每個後續分店主要是內容遷移和主題配置,需要 1-2 週。我們通常分批啟動——一次 3-4 個分店。
個別分店是否仍然可以有其他分店沒有的唯一頁面? 絕對。內容模型支援特定於分店的頁面類型。如果您的度假村分店需要「婚禮場地」部分,但您的商務飯店不需要,這是內容級決策。CMS 架構支援可選頁面類型,Next.js 動態路由可處理為給定分店呈現任何存在於 CMS 中的頁面。
當您收購新飯店並需要將其添加到平台時會發生什麼? 這是最大的好處之一。添加新分店意味著:在 CMS 中建立分店項目、配置主題(顏色、字體、徽標)、添加網域對應和填充內容。一個有能力的內容團隊可以在 1-2 週內讓新分店上線。相比之下,建立一個獨立網站需要 2-3 個月。
您如何在不同國家/地區的分店之間處理多語言支援? Next.js 內置了 i18n 路由支援。結合支援本地化內容的無頭 CMS(Sanity 和 Contentful 都支援此功能),您可以在其相關語言中為每個分店提供服務。巴塞隆納的分店可能需要西班牙語、加泰隆語、英語和法語。邁阿密的分店可能只需要英語和西班牙語。每個分店的語言配置是獨立的。
這個架構是否可以與 Astro 而不是 Next.js 一起運行? 是的,對於某些飯店集團來說,它實際上是更好的選擇。如果您的分店主要是內容驅動,互動性最少(例如,沒有複雜的預訂流程),Astro 的多頁架構 可以透過更少的 JavaScript 實現甚至更好的效能。多租戶模式是相似的——基於中介軟體的分店解析、具有分店範圍的無頭 CMS、每個分店的主題代幣。
當分店在單獨的網域上但從一個應用程式提供時,您如何處理 SEO? 每個分店網域都有自己的網站地圖、自己的 robots.txt、自己的結構化數據(飯店架構標記)和自己的中繼標籤。從 Google 的角度來看,這些都是完全獨立的網站。規範 URL 指向每個分店自己的網域。您還可以獲得集中式架構標記生成的好處——每個分店都會自動獲得飯店、房間、評論和當地企業資訊的適當 JSON-LD。
如果分店有特定的整合,如當地活動預訂或水療預訂系統,怎麼辦? 元件架構支援分店級整合配置。CMS 中的每個分店配置都可以指定它使用的第三方整合。渲染層有條件地包含這些整合元件。水療分店獲得水療預訂小工具。市中心商務飯店獲得會議室配置器。這些作為動態匯入加載,因此它們不會影響不使用它們的分店的包大小。
一個分店的流量尖峰是否存在影響其他分店的風險? 在 Vercel 或 Cloudflare Pages 等平台上,實際上沒有。這些邊緣平台旨在處理流量尖峰。靜態頁面從 CDN 快取提供,因此一個分店的尖峰不會消耗會影響另一個分店的伺服器資源。對於動態路由(如實時可用性檢查),您需要按分店的速率限制,以防止一個分店的病毒時刻耗盡您的預訂引擎 API 配額。但這是 API 級別的問題,而不是主機問題。