(阶段一:设计)面向商户,多模板 + 多模块的 Headless CMS 架构范式设计

多模板 + 多模块的 Headless CMS 架构范式设计

本文记录了我基于实际业务需求,思考并设计的一套面向商户的低代码页面配置系统 —— 一个支持多模板、多模块、可视化配置的 Headless CMS 架构。

一. 背景

在开发 ShopStack 过程中,我希望让商户能够:

  • 自主选择模板(如酷炫风 Cool、通用风 General)
  • 在模板内部自由组合页面模块(Hero Section、Category Section 等)
  • 实时预览 + 配置文案/图片内容

从而形成一个面向商户的低代码模板系统

目标不是 Webflow 这类开发者工具,而是像 Shopify Theme Editor 一样,让非技术用户也能配置官网界面

为此,我从建模、数据流、前端注册机制等多个维度进行了深入设计。


二. 根基概念: 三层模型

整个架构以三层结构为核心:

Tenant -> ThemeOptions -> PageInstance -> SectionInstance

页面结构建议:Page Sections(大模块)

层级 说明
ThemeOptions 商户所选模板和页面映射配置
PageInstance 页面级别的容器(如 landing、shopping)
SectionInstance 每个页面内的模块配置(如 HeroSection)
Section 名称 说明
HeroSection 页首大图 + 主标题 + CTA 按钮等
FeaturesSection 介绍功能/特点(如三列图标+文字说明)
TestimonialsSection 用户评价/推荐语
CarouselSection 滚动展示项目/图文/客户等
StepsSection 展示“如何使用/三步走”等流程内容
PricingSection 展示套餐或付费选项
FAQSection 常见问题解答区域
CallToActionSection 收尾 CTA(如“现在开始”按钮)
FooterSection 页脚链接/版权/社媒

每个 Section 内部建议的原子组件

组件名(Component) 说明
PrimaryTitle 一级标题(大标题,吸睛)
SecondaryTitle 二级标题(副标题)
Paragraph 通用段落文本
FeatureCard 图标 + 标题 + 描述(如三个功能介绍)
IconBullet 图标加一句话(适合小功能点列举)
TestimonialCard 用户头像 + 名字 + 引语
StepCard 数字步骤 + 简短说明
CarouselItem 可滚动卡片项
CTAButton 带样式的 Call To Action 按钮
GradientOverlay 用于 hero section 背景叠加的渐变层
BackgroundImage 背景图片(通常绝对定位)

三. ThemeOptions 设计

interface ThemeOptions {
  tenantId: string;
  themeId: string; // "cool", "general"
  themeVariant?: string;
  pageInstanceMap: Record<string, string | null>; // landing: "pageId1"
  updatedAt: Date;
  version?: number;
}

设计要点

  • themeId 考虑名称规范化,无不同字面
  • pageInstanceMap是指各类页面映射到对应的 PageInstance
  • version是为未来做历史版本存储预留扩展合理

四. PageInstance & SectionInstance 建模

interface PageInstance {
  id: string;
  pageType: string; // 如 landing
  sections: {
    sectionId: string;
    layout?: string;
    order?: number;
  }[];
}

interface SectionInstance {
  id: string;
  sectionName: string; // HeroSection
  props: Record<string, any>; // 对应 jsonb 字段
}

设计要点

  • sections为结构体数组,支持拖拽排序和 layout
  • props使用 PostgreSQL jsonb存储,高度灵活

五. SectionRegistry 前端注册机制

用于指导 UI 编辑器生成对应的表单结构

常规写法:

const SectionRegistry = {
  cool: {
    landing: {
      HeroSection: {
        primaryText: "string",
        secondaryText: "string",
        imageUrl: "string"
      }
    }
  }
}

推荐写法:注册列表形式,选择性更好

type SectionRegistryEntry = {
  templateId: string;
  pageType: string;
  sectionName: string;
  schema: Record<string, string>;
};

const SectionRegistry: SectionRegistryEntry[] = [
  {
    templateId: "cool",
    pageType: "landing",
    sectionName: "HeroSection",
    schema: {
      primaryText: "string",
      secondaryText: "string",
      imageUrl: "string"
    }
  }
];

利点

  • 适配 plugin 模式,便于后期扩展
  • 可和后端配置表/文件相关联

六. 保存逻辑

简单方案: 清空重写 (Soft Reset)

  1. 清空 ThemeOptions 中当前 tenantId 下的 pageInstanceMap
  2. 利用级联删除 (ON DELETE CASCADE),从而删掉相关 PageInstance 和 SectionInstance
  3. 重新 insert SectionInstance
  4. 重新 insert PageInstance
  5. 重新 update ThemeOptions

可选扩展:版本史

  • 使用 versionupdatedAt等字段做历史存档
  • 为后期回滚配置/历史系统基础

七. 后续行动

🔄 页面间实时预览联动(预研方向)
前端配置页面和预览页面通过 BroadcastChannel 实现跨 tab 联动:

表单发生 onChange 事件后 debounce

使用 channel.postMessage(formState) 广播整个 section 配置

预览页订阅 channel.onmessage 自动热更新 UI

具体:

  • 页面 A 是配置页,用户点了勾、选了图片

  • 页面 B 是实时预览页,能收到变化同步 UI

'use client'

import { useEffect, useState } from 'react'

export default function PreviewUpdater() {
  const [data, setData] = useState(null)

  useEffect(() => {
    const channel = new BroadcastChannel("preview_channel")

    channel.onmessage = (event) => {
      console.log("🟢 收到更新:", event.data)
      setData(event.data) // 更新本地状态
    }

    return () => channel.close()
  }, [])

  return <MyPreviewUI data={data} />
}

技术路径:新标签页打开 + BroadcastChannel 联动

// 在配置页中点击“预览”
const onPreviewClick = () => {
  window.open('/preview', '_blank') // 打开预览页
}
// 发送实时配置
const channel = new BroadcastChannel("preview_channel")

const onFormChange = (formState) => {
  channel.postMessage(formState)
}


💡 总结

本技术方案的特点:

范式 描述
Headless CMS 商户通过结构化数据组合页面
低代码平台 配置即 UI,模块可视化编辑
多模板/多模块 解耦主题与组件,支持扩展
动态 schema 注册 前端驱动配置 UI 表单
jsonb + RDB 混合建模 保留自由度同时保障结构化查询
posted @ 2025-07-18 11:23  PEAR2020  阅读(25)  评论(0)    收藏  举报