酒店集團網站:使用 Next.js 的多物業架構
管理酒店集團網站:使用 Next.js 的多物業架構
管理一個酒店網站很簡單。管理三十個?這就是大多數團隊開始做出他們會後悔多年的決定的地方。我見過酒店集團將各個 WordPress 安裝拼湊在一起,將頁面生成器用膠帶粘貼到單一 CMS 平台上,以及花費六位數預算購買企業解決方案,但仍然無法在三個月內完成新物業上線。
還有更好的方法。一個經過正確架構設計的 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、輕鬆的物業隔離、對 SEO 有益(如果物業不需要單獨的 TLD)。
缺點: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 個域名(截至 2025 年為 $20/月)。對於較大的投資組合,他們的企業計劃移除該限制。
優點:完整的品牌獨立性、保留現有域名權益。
缺點:DNS 管理開銷、更複雜的 SSL 配置。
模式 3:基於路徑的路由
一個域名下的所有物業:hotelgroup.com/properties/grand-plaza、hotelgroup.com/properties/seaside-resort。
優點:最簡單實現、整合域名權威用於 SEO。
缺點:每個物業的品牌身份較低、URL 結構感覺像企業的。
| 模式 | 品牌獨立性 | SEO 靈活性 | 實現複雜性 | 最佳用途 |
|---|---|---|---|---|
| 子域名 | 中等 | 中等 | 低 | 預算有限的集團 |
| 自訂域名 | 高 | 高 | 中等 | 成熟的品牌 |
| 基於路徑 | 低 | 高(整合) | 最低 | 新投資組合網站 |
我們合作過的大多數酒店集團最終選擇自訂域名對應。物業在其域名中擁有品牌權益,營銷團隊希望獨立性。

酒店集團的無頭 CMS 策略
CMS 的選擇決定了這個架構的成敗。您需要一個在內容級別支持多租戶的系統——A 物業的編輯不能意外修改 B 物業的內容,但企業管理員可以管理所有內容。
效果良好的 CMS 選項
Sanity 是我為酒店集團的首選。其文檔級別的權限、自訂工作室配置和 GROQ 查詢語言使物業範圍的內容檢索變得微不足道。您可以構建一個帶有每個物業工作區視圖的 Sanity Studio。定價從團隊計劃的 $99/月起(2025 年定價),並且可擴展到大型內容量。
Contentful 在您已經在其生態系統中時有效。他們的空間級別隔離很好地對應物業,儘管成本可能很高——高級計劃上的每個空間都會增加成本,對於企業級酒店集團需求,您需要花費 $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';
}
Tailwind CSS v4(2025 年發佈)通過其 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,並僅為付款轉交到訂房引擎。更多工作,更好的 UX。
選項 2 是 Next.js 架構真正閃耀的地方。您可以為每個物業構建一個漂亮、快速、品牌內的訂房體驗,感覺像原生應用。服務器組件可以預取可用性數據。訂房流程保留在您的域名上,這對轉換追蹤和 SEO 更好。
域名路由和物業解析
物業解析流程需要快速。非常快速。它在每個請求上運行。
以下是在生產中有效的模式:
- 邊緣中間件解析域名 → 物業 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 非常適合此用途——它是一個全球分佈的鍵值存儲,讀取延遲不到 1ms。它在 Pro 計劃上對最高 512KB 數據免費,這對物業查詢表來說足夠了。
大規模性能
酒店網站具有重要的特定性能特徵:
- 圖像密集型頁面:客房畫廊、物業照片、目的地圖像
- 季節性流量尖峰:假期、會議季節、本地事件
- 全球受眾:來自世界各地瀏覽的國際旅客
- 轉換關鍵:每延遲 100ms 都會損失訂房
靜態生成策略
為物業頁面使用增量靜態再生 (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.5s
- CLS 為 0(無圖像加載的佈局移位)
- 初始加載時總頁面重量低於 1.5MB
集中管理儀表板
除了 CMS 外,酒店集團還需要運營儀表板。這是您構建自訂工具的地方:
- 物業概況:每個物業網站的狀態(上線、暫存、維護)
- 內容新鮮度:哪些物業尚未更新其季節內容
- 性能監控:每個物業的核心網頁指標
- 分析匯總:跨所有物業的訂房漏斗指標
我們通常將其構建為一個單獨的 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 中創建物業條目、配置主題(顏色、字體、徽標)、添加域名對應和填充內容。一個有能力的內容團隊可以在 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 級別的考慮,而不是託管考慮。