欢迎光临
我们一直在努力

前端样式架构全景:场景分类、技术方案与选型指南

前端样式架构全景:场景分类、技术方案与选型指南

定位:面向电商及类似动态化系统,系统梳理"哪些场景前端写、哪些服务端下发",枚举业界全部主流架构方案,给出选型矩阵与设计规范。


一、核心问题

在前后端分离架构中,CSS 样式的控制权该如何分配?这个问题的答案不是非此即彼,而是一个从"纯前端"到"纯服务端"的连续光谱,不同业务场景落在光谱的不同位置。

◄─────────────────────────────────────────────────────────────────►
纯前端静态 混合治理 纯服务端下发
(传统 Web) (大多数电商系统) (Server-Driven UI)

● 静态 CSS 文件 ● 色值/刻度服务端下发 ● 服务端返回完整
● 前端完全自主 ● 布局/动画前端自主 渲染指令
● 无动态能力 ● 令牌桥接 ● 前端仅渲染壳
● 主题前端切换

关键洞察:绝大多数电商系统落在"混合治理"区间,但具体哪些放前端、哪些放服务端,缺乏统一规范,导致"样式下沉"无节制蔓延。


二、场景分类:前端写 vs 服务端下发

2.1 决策模型

面对任何一个样式属性,通过四问法确定归属:

#mermaid-svg-SMFjJjvHIvmqIePv{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-SMFjJjvHIvmqIePv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-SMFjJjvHIvmqIePv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-SMFjJjvHIvmqIePv .error-icon{fill:#552222;}#mermaid-svg-SMFjJjvHIvmqIePv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-SMFjJjvHIvmqIePv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-SMFjJjvHIvmqIePv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-SMFjJjvHIvmqIePv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-SMFjJjvHIvmqIePv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-SMFjJjvHIvmqIePv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-SMFjJjvHIvmqIePv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-SMFjJjvHIvmqIePv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-SMFjJjvHIvmqIePv .marker.cross{stroke:#333333;}#mermaid-svg-SMFjJjvHIvmqIePv svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-SMFjJjvHIvmqIePv p{margin:0;}#mermaid-svg-SMFjJjvHIvmqIePv .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-SMFjJjvHIvmqIePv .cluster-label text{fill:#333;}#mermaid-svg-SMFjJjvHIvmqIePv .cluster-label span{color:#333;}#mermaid-svg-SMFjJjvHIvmqIePv .cluster-label span p{background-color:transparent;}#mermaid-svg-SMFjJjvHIvmqIePv .label text,#mermaid-svg-SMFjJjvHIvmqIePv span{fill:#333;color:#333;}#mermaid-svg-SMFjJjvHIvmqIePv .node rect,#mermaid-svg-SMFjJjvHIvmqIePv .node circle,#mermaid-svg-SMFjJjvHIvmqIePv .node ellipse,#mermaid-svg-SMFjJjvHIvmqIePv .node polygon,#mermaid-svg-SMFjJjvHIvmqIePv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-SMFjJjvHIvmqIePv .rough-node .label text,#mermaid-svg-SMFjJjvHIvmqIePv .node .label text,#mermaid-svg-SMFjJjvHIvmqIePv .image-shape .label,#mermaid-svg-SMFjJjvHIvmqIePv .icon-shape .label{text-anchor:middle;}#mermaid-svg-SMFjJjvHIvmqIePv .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-SMFjJjvHIvmqIePv .rough-node .label,#mermaid-svg-SMFjJjvHIvmqIePv .node .label,#mermaid-svg-SMFjJjvHIvmqIePv .image-shape .label,#mermaid-svg-SMFjJjvHIvmqIePv .icon-shape .label{text-align:center;}#mermaid-svg-SMFjJjvHIvmqIePv .node.clickable{cursor:pointer;}#mermaid-svg-SMFjJjvHIvmqIePv .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-SMFjJjvHIvmqIePv .arrowheadPath{fill:#333333;}#mermaid-svg-SMFjJjvHIvmqIePv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-SMFjJjvHIvmqIePv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-SMFjJjvHIvmqIePv .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SMFjJjvHIvmqIePv .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-SMFjJjvHIvmqIePv .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SMFjJjvHIvmqIePv .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-SMFjJjvHIvmqIePv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-SMFjJjvHIvmqIePv .cluster text{fill:#333;}#mermaid-svg-SMFjJjvHIvmqIePv .cluster span{color:#333;}#mermaid-svg-SMFjJjvHIvmqIePv div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-SMFjJjvHIvmqIePv .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-SMFjJjvHIvmqIePv rect.text{fill:none;stroke-width:0;}#mermaid-svg-SMFjJjvHIvmqIePv .icon-shape,#mermaid-svg-SMFjJjvHIvmqIePv .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SMFjJjvHIvmqIePv .icon-shape p,#mermaid-svg-SMFjJjvHIvmqIePv .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-SMFjJjvHIvmqIePv .icon-shape .label rect,#mermaid-svg-SMFjJjvHIvmqIePv .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SMFjJjvHIvmqIePv .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-SMFjJjvHIvmqIePv .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-SMFjJjvHIvmqIePv :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

值:色值/刻度/URL

规则:布局/动画/排版

样式属性归属判断

Q1: 运营是否需要不发版就修改?

Q2: 是否与设备/平台/屏幕相关?

Q3: 修改后是否改变 UI 语义?

🟢 前端写

Q4: 是否属于设计规范刻度?

Q5: 只是微调(如间距±2px)?

Q6: 是'值'还是'规则'?

🔴 服务端下发

2.2 场景全表:30 个典型场景的归属判定

🔴 服务端下发(14 个场景)
#场景属性类型下发形式变更驱动典型案例
S1 品牌主色 颜色值 令牌 brand-primary: #FF0F23 品牌升级 京东红、淘宝橙
S2 价格色 颜色值 令牌 price-main: #FF0F23 A/B 实验 促销价、会员价颜色
S3 促销标签色 颜色值 令牌 tag-danger: #FA2C19 运营策略 限时抢、满减标签
S4 语义色(成功/警告/错误) 颜色值 令牌 color-success: #07C160 设计规范 库存充足、售罄
S5 楼层/卡片背景色 颜色值 令牌 card-bg: #FFFFFF 大促换肤 618 黑金主题
S6 按钮圆角刻度 尺寸值 令牌 radius-lg: 12px 设计规范升级 圆角风格统一
S7 卡片圆角刻度 尺寸值 令牌 radius-md: 8px 设计规范升级 卡片风格统一
S8 字号刻度 尺寸值 令牌 font-size-title: 18px 设计规范升级 字号体系调整
S9 字重刻度 关键字 令牌 font-weight-bold: 600 设计规范升级 字重体系调整
S10 图标/插画 URL 资源引用 URL 字符串 运营/设计 楼层头图标、空态插画
S11 按钮文案 文案 字符串 运营/法务 “立即购买"→"立即抢购”
S12 提示文案 文案 字符串 运营/法务 限购提示、配送说明
S13 暗黑模式色值集 颜色值集 tokenSetDark 系统主题 暗色适配
S14 A/B 实验标识 枚举 实验组 ID 实验平台 前端根据 ID 选样式
🟢 前端写(12 个场景)
#场景属性类型实现方式理由典型案例
F1 组件布局策略 CSS 规则 Flexbox/Grid 设备/屏幕相关 横排/竖排切换
F2 组件内部间距 CSS 规则 margin/padding/gap 组件内部自治 图标与文字间距
F3 组件尺寸 CSS 规则 width/height/min-height 设备 DPI 相关 按钮高度、图标尺寸
F4 排版规则 CSS 规则 line-height/text-overflow 排版是前端专长 多行截断、省略号
F5 过渡动画 CSS 规则 transition 性能相关 按钮点击反馈
F6 关键帧动画 CSS 规则 @keyframes 性能相关 骨架屏 shimmer
F7 响应式断点 CSS 规则 @media / container-query 屏幕相关 折叠屏适配
F8 安全区域 CSS 规则 safe-area-inset-* 设备相关 刘海屏适配
F9 字体栈 CSS 规则 font-family 平台相关 iOS/Android 字体
F10 交互状态 CSS 规则 :hover/:active/:focus 交互相关 按钮按压态
F11 加载态 CSS 规则 骨架屏/占位符 加载体验 数据加载中
F12 滚动行为 CSS 规则 overflow/scroll-snap 交互相关 楼层滚动吸顶
🟡 协商区(4 个场景)
#场景服务端角色前端角色协商策略判定依据
N1 楼层间距 下发刻度 spacing-md 自主决定像素值 刻度下发,像素前端算 大促期间距可能调整
N2 弹层高度比例 下发比例 75% 自主决定 min/max 比例下发,边界前端 不同弹层高度不同
N3 截断行数 一般不下发 自主决定 除非运营需动态控制 运营可能要求"大促时显示3行"
N4 阴影层级 下发刻度 shadow-sm 自主决定具体值 刻度下发,值前端定 设计规范升级时统一

2.3 速查:一秒判断

如果它是…归属原因
一个颜色值 🔴 服务端 运营杠杆
一个图标 URL 🔴 服务端 资源运营
一段文案 🔴 服务端 法务/运营
一个圆角刻度 🔴 服务端 设计规范
一个字号刻度 🔴 服务端 设计规范
一个 margin 值 🟢 前端 布局属性
一个 height 值 🟢 前端 设备相关
一个 line-height 🟢 前端 排版规则
一个 font-family 🟢 前端 平台相关
一个 animation 🟢 前端 性能相关
一个 display 值 🟢 前端 布局策略
一个 text-overflow 🟢 前端 CSS 规则

三、技术架构全景:6 种主流方案

架构总览

#mermaid-svg-CcMC0I9VoN84MJ4e{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CcMC0I9VoN84MJ4e .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CcMC0I9VoN84MJ4e .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CcMC0I9VoN84MJ4e .error-icon{fill:#552222;}#mermaid-svg-CcMC0I9VoN84MJ4e .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CcMC0I9VoN84MJ4e .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CcMC0I9VoN84MJ4e .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CcMC0I9VoN84MJ4e .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CcMC0I9VoN84MJ4e .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CcMC0I9VoN84MJ4e .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CcMC0I9VoN84MJ4e .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CcMC0I9VoN84MJ4e .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CcMC0I9VoN84MJ4e .marker.cross{stroke:#333333;}#mermaid-svg-CcMC0I9VoN84MJ4e svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CcMC0I9VoN84MJ4e p{margin:0;}#mermaid-svg-CcMC0I9VoN84MJ4e .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-CcMC0I9VoN84MJ4e .cluster-label text{fill:#333;}#mermaid-svg-CcMC0I9VoN84MJ4e .cluster-label span{color:#333;}#mermaid-svg-CcMC0I9VoN84MJ4e .cluster-label span p{background-color:transparent;}#mermaid-svg-CcMC0I9VoN84MJ4e .label text,#mermaid-svg-CcMC0I9VoN84MJ4e span{fill:#333;color:#333;}#mermaid-svg-CcMC0I9VoN84MJ4e .node rect,#mermaid-svg-CcMC0I9VoN84MJ4e .node circle,#mermaid-svg-CcMC0I9VoN84MJ4e .node ellipse,#mermaid-svg-CcMC0I9VoN84MJ4e .node polygon,#mermaid-svg-CcMC0I9VoN84MJ4e .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CcMC0I9VoN84MJ4e .rough-node .label text,#mermaid-svg-CcMC0I9VoN84MJ4e .node .label text,#mermaid-svg-CcMC0I9VoN84MJ4e .image-shape .label,#mermaid-svg-CcMC0I9VoN84MJ4e .icon-shape .label{text-anchor:middle;}#mermaid-svg-CcMC0I9VoN84MJ4e .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CcMC0I9VoN84MJ4e .rough-node .label,#mermaid-svg-CcMC0I9VoN84MJ4e .node .label,#mermaid-svg-CcMC0I9VoN84MJ4e .image-shape .label,#mermaid-svg-CcMC0I9VoN84MJ4e .icon-shape .label{text-align:center;}#mermaid-svg-CcMC0I9VoN84MJ4e .node.clickable{cursor:pointer;}#mermaid-svg-CcMC0I9VoN84MJ4e .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CcMC0I9VoN84MJ4e .arrowheadPath{fill:#333333;}#mermaid-svg-CcMC0I9VoN84MJ4e .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CcMC0I9VoN84MJ4e .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CcMC0I9VoN84MJ4e .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CcMC0I9VoN84MJ4e .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CcMC0I9VoN84MJ4e .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CcMC0I9VoN84MJ4e .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CcMC0I9VoN84MJ4e .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CcMC0I9VoN84MJ4e .cluster text{fill:#333;}#mermaid-svg-CcMC0I9VoN84MJ4e .cluster span{color:#333;}#mermaid-svg-CcMC0I9VoN84MJ4e div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-CcMC0I9VoN84MJ4e .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CcMC0I9VoN84MJ4e rect.text{fill:none;stroke-width:0;}#mermaid-svg-CcMC0I9VoN84MJ4e .icon-shape,#mermaid-svg-CcMC0I9VoN84MJ4e .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CcMC0I9VoN84MJ4e .icon-shape p,#mermaid-svg-CcMC0I9VoN84MJ4e .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CcMC0I9VoN84MJ4e .icon-shape .label rect,#mermaid-svg-CcMC0I9VoN84MJ4e .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CcMC0I9VoN84MJ4e .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CcMC0I9VoN84MJ4e .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CcMC0I9VoN84MJ4e :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

方案六:Server-Driven UI

服务端下发完整 UI 指令

前端通用渲染器

前端零样式代码

方案五:模板化渲染

服务端下发模板 + 数据

前端模板引擎渲染

样式随模板走

方案四:组件级样式下发

服务端下发 ComponentStyle

前端 style 属性绑定

运营完全控制

方案三:Design Tokens 体系

设计侧定义令牌

Style Dictionary 构建

多平台输出

方案二:CSS 变量 + 配置注入

服务端下发色值

前端 CSS 变量映射

组件 var() 消费

方案一:纯前端静态

CSS/SCSS 文件

CSS 变量主题


方案一:纯前端静态

架构:CSS/SCSS 文件 → 构建工具 → 静态 CSS
动态性:无
服务端角色:不参与样式

适用场景:

  • 内容型网站(博客、文档站)
  • 后台管理系统
  • 品牌官网(不换肤)
  • 组件库/设计系统的静态部分

优点:

  • ✅ 最简单,前端完全自主
  • ✅ 构建时优化,性能最优
  • ✅ 样式与逻辑解耦
  • ✅ CSS 可缓存、可 CDN

缺点:

  • ❌ 无法动态换肤
  • ❌ 无法 A/B 实验切换样式
  • ❌ 修改任何颜色需要发版

代码示例:

/* 静态 CSS 文件 — 色值写死 */
.price-label {
color: #FF0F23;
font-size: 14px;
line-height: 1.4;
}


方案二:CSS 变量 + 配置注入

架构:服务端下发色值 → JS 注入 CSS 变量 → 组件 var() 消费
动态性:色值/刻度可动态调整
服务端角色:下发设计决策的值

适用场景:

  • 大多数电商系统(推荐方案)
  • 需要运营动态调色的商详页
  • 需要暗黑模式适配
  • 需要大促换肤但不需要改布局

优点:

  • ✅ 色值可动态调整,支持 A/B 实验
  • ✅ 暗黑模式前端自主,不翻倍配置
  • ✅ 布局/动画/排版前端自主,设备适配自由
  • ✅ 改动量适中,渐进式迁移

缺点:

  • ❌ 需要前端建立 CSS 变量层
  • ❌ 需要前后端对齐令牌名
  • ❌ 旧代码迁移有成本

架构图:

#mermaid-svg-cbjlECBuehxekK85{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-cbjlECBuehxekK85 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-cbjlECBuehxekK85 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-cbjlECBuehxekK85 .error-icon{fill:#552222;}#mermaid-svg-cbjlECBuehxekK85 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cbjlECBuehxekK85 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-cbjlECBuehxekK85 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cbjlECBuehxekK85 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cbjlECBuehxekK85 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-cbjlECBuehxekK85 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cbjlECBuehxekK85 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cbjlECBuehxekK85 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cbjlECBuehxekK85 .marker.cross{stroke:#333333;}#mermaid-svg-cbjlECBuehxekK85 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cbjlECBuehxekK85 p{margin:0;}#mermaid-svg-cbjlECBuehxekK85 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-cbjlECBuehxekK85 .cluster-label text{fill:#333;}#mermaid-svg-cbjlECBuehxekK85 .cluster-label span{color:#333;}#mermaid-svg-cbjlECBuehxekK85 .cluster-label span p{background-color:transparent;}#mermaid-svg-cbjlECBuehxekK85 .label text,#mermaid-svg-cbjlECBuehxekK85 span{fill:#333;color:#333;}#mermaid-svg-cbjlECBuehxekK85 .node rect,#mermaid-svg-cbjlECBuehxekK85 .node circle,#mermaid-svg-cbjlECBuehxekK85 .node ellipse,#mermaid-svg-cbjlECBuehxekK85 .node polygon,#mermaid-svg-cbjlECBuehxekK85 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-cbjlECBuehxekK85 .rough-node .label text,#mermaid-svg-cbjlECBuehxekK85 .node .label text,#mermaid-svg-cbjlECBuehxekK85 .image-shape .label,#mermaid-svg-cbjlECBuehxekK85 .icon-shape .label{text-anchor:middle;}#mermaid-svg-cbjlECBuehxekK85 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-cbjlECBuehxekK85 .rough-node .label,#mermaid-svg-cbjlECBuehxekK85 .node .label,#mermaid-svg-cbjlECBuehxekK85 .image-shape .label,#mermaid-svg-cbjlECBuehxekK85 .icon-shape .label{text-align:center;}#mermaid-svg-cbjlECBuehxekK85 .node.clickable{cursor:pointer;}#mermaid-svg-cbjlECBuehxekK85 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-cbjlECBuehxekK85 .arrowheadPath{fill:#333333;}#mermaid-svg-cbjlECBuehxekK85 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-cbjlECBuehxekK85 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-cbjlECBuehxekK85 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cbjlECBuehxekK85 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-cbjlECBuehxekK85 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cbjlECBuehxekK85 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-cbjlECBuehxekK85 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-cbjlECBuehxekK85 .cluster text{fill:#333;}#mermaid-svg-cbjlECBuehxekK85 .cluster span{color:#333;}#mermaid-svg-cbjlECBuehxekK85 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-cbjlECBuehxekK85 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-cbjlECBuehxekK85 rect.text{fill:none;stroke-width:0;}#mermaid-svg-cbjlECBuehxekK85 .icon-shape,#mermaid-svg-cbjlECBuehxekK85 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cbjlECBuehxekK85 .icon-shape p,#mermaid-svg-cbjlECBuehxekK85 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-cbjlECBuehxekK85 .icon-shape .label rect,#mermaid-svg-cbjlECBuehxekK85 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cbjlECBuehxekK85 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-cbjlECBuehxekK85 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-cbjlECBuehxekK85 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

前端

服务端

策略匹配

JSON 响应

setProperty

var

覆盖

配置中心/Apollo/Nacos

APItokenSet + tokenSetDark

JS 注入层tokenSet → CSS 变量

CSS 变量层:root { –token: value }

组件层var(–token)

主题层@media dark

代码示例:

// 后端:令牌集 VO
public class TokenSetVO {
private Map<String, String> tokenSet; // 亮色令牌集
private Map<String, String> tokenSetDark; // 暗色令牌集(可选)
}

{
"_comment": "API 响应示例",
"tokenSet": {
"price-main-color": "#FF0F23",
"price-second-color": "#888B94",
"card-radius": "8px"
},
"tokenSetDark": {
"price-main-color": "#FF6B6B"
}
}

// 前端:JS 注入层
function injectTokens(tokenSet, tokenSetDark) {
const root = document.documentElement;
// 注入亮色令牌
Object.entries(tokenSet).forEach(([name, value]) => {
root.style.setProperty(`${name}`, value);
});
// 注入暗色令牌(通过 CSS 媒体查询自动切换)
if (tokenSetDark) {
const style = document.createElement('style');
style.textContent = `@media (prefers-color-scheme: dark) { :root { ${
Object.entries(tokenSetDark)
.map(([k, v]) => `${k}: ${v};`)
.join('')
}
} }`
;
document.head.appendChild(style);
}
}

/* 前端:组件消费 */
.price-label {
color: var(–price-main-color, #FF0F23); /* 令牌 + 兜底值 */
font-size: 14px; /* 前端自主 */
line-height: 1.4; /* 前端自主 */
margin-left: 8px; /* 前端自主 */
}


方案三:Design Tokens 体系

架构:设计侧 → 令牌 JSON → Style Dictionary → 多平台输出
动态性:全链路可追溯,设计变更自动同步
服务端角色:消费令牌 JSON,下发语义令牌

适用场景:

  • 大型设计系统(多端统一)
  • 设计团队成熟,有 Figma Tokens 工作流
  • 需要设计-开发全链路对齐
  • 多品牌/多主题体系

优点:

  • ✅ 设计-开发单一信源
  • ✅ 令牌变更自动同步到所有平台
  • ✅ 多端(Web/iOS/Android)一致性保障
  • ✅ 令牌可版本化、可审计

缺点:

  • ❌ 全链路改造成本高
  • ❌ 需要设计团队配合
  • ❌ Style Dictionary 有学习曲线
  • ❌ 对现有系统侵入性强

架构图:

#mermaid-svg-IOnvu8dSFyMK4aGr{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-IOnvu8dSFyMK4aGr .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-IOnvu8dSFyMK4aGr .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-IOnvu8dSFyMK4aGr .error-icon{fill:#552222;}#mermaid-svg-IOnvu8dSFyMK4aGr .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-IOnvu8dSFyMK4aGr .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-IOnvu8dSFyMK4aGr .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-IOnvu8dSFyMK4aGr .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-IOnvu8dSFyMK4aGr .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-IOnvu8dSFyMK4aGr .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-IOnvu8dSFyMK4aGr .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-IOnvu8dSFyMK4aGr .marker{fill:#333333;stroke:#333333;}#mermaid-svg-IOnvu8dSFyMK4aGr .marker.cross{stroke:#333333;}#mermaid-svg-IOnvu8dSFyMK4aGr svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-IOnvu8dSFyMK4aGr p{margin:0;}#mermaid-svg-IOnvu8dSFyMK4aGr .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-IOnvu8dSFyMK4aGr .cluster-label text{fill:#333;}#mermaid-svg-IOnvu8dSFyMK4aGr .cluster-label span{color:#333;}#mermaid-svg-IOnvu8dSFyMK4aGr .cluster-label span p{background-color:transparent;}#mermaid-svg-IOnvu8dSFyMK4aGr .label text,#mermaid-svg-IOnvu8dSFyMK4aGr span{fill:#333;color:#333;}#mermaid-svg-IOnvu8dSFyMK4aGr .node rect,#mermaid-svg-IOnvu8dSFyMK4aGr .node circle,#mermaid-svg-IOnvu8dSFyMK4aGr .node ellipse,#mermaid-svg-IOnvu8dSFyMK4aGr .node polygon,#mermaid-svg-IOnvu8dSFyMK4aGr .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-IOnvu8dSFyMK4aGr .rough-node .label text,#mermaid-svg-IOnvu8dSFyMK4aGr .node .label text,#mermaid-svg-IOnvu8dSFyMK4aGr .image-shape .label,#mermaid-svg-IOnvu8dSFyMK4aGr .icon-shape .label{text-anchor:middle;}#mermaid-svg-IOnvu8dSFyMK4aGr .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-IOnvu8dSFyMK4aGr .rough-node .label,#mermaid-svg-IOnvu8dSFyMK4aGr .node .label,#mermaid-svg-IOnvu8dSFyMK4aGr .image-shape .label,#mermaid-svg-IOnvu8dSFyMK4aGr .icon-shape .label{text-align:center;}#mermaid-svg-IOnvu8dSFyMK4aGr .node.clickable{cursor:pointer;}#mermaid-svg-IOnvu8dSFyMK4aGr .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-IOnvu8dSFyMK4aGr .arrowheadPath{fill:#333333;}#mermaid-svg-IOnvu8dSFyMK4aGr .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-IOnvu8dSFyMK4aGr .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-IOnvu8dSFyMK4aGr .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IOnvu8dSFyMK4aGr .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-IOnvu8dSFyMK4aGr .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IOnvu8dSFyMK4aGr .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-IOnvu8dSFyMK4aGr .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-IOnvu8dSFyMK4aGr .cluster text{fill:#333;}#mermaid-svg-IOnvu8dSFyMK4aGr .cluster span{color:#333;}#mermaid-svg-IOnvu8dSFyMK4aGr div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-IOnvu8dSFyMK4aGr .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-IOnvu8dSFyMK4aGr rect.text{fill:none;stroke-width:0;}#mermaid-svg-IOnvu8dSFyMK4aGr .icon-shape,#mermaid-svg-IOnvu8dSFyMK4aGr .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IOnvu8dSFyMK4aGr .icon-shape p,#mermaid-svg-IOnvu8dSFyMK4aGr .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-IOnvu8dSFyMK4aGr .icon-shape .label rect,#mermaid-svg-IOnvu8dSFyMK4aGr .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IOnvu8dSFyMK4aGr .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-IOnvu8dSFyMK4aGr .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-IOnvu8dSFyMK4aGr :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

令牌 JSON

CSS Variables

Swift 代码

XML 资源

JSON

语义令牌

tokenSet

Figma设计稿

Figma Tokens插件

Style Dictionary构建

Web 端

iOS 端

Android 端

配置中心/Apollo

BF API

前端

令牌 JSON 示例:

{
"color": {
"red": { "500": { "$value": "#FF0F23", "$type": "color" } },
"gray": { "400": { "$value": "#888B94", "$type": "color" } }
},
"semantic": {
"price-main-color": { "$value": "{color.red.500}", "$type": "color" },
"price-second-color": { "$value": "{color.gray.400}", "$type": "color" }
}
}


方案四:组件级样式下发

架构:服务端下发 ComponentStyle(CSS 属性级)→ 前端 style 绑定
动态性:运营完全控制每个组件的每个样式
服务端角色:下发 CSS 属性名 + 值

适用场景:

  • 高度运营驱动的页面(活动页、大促会场)
  • 需要运营在后台拖拽调整样式的场景
  • 前端能力弱,需要服务端兜底

优点:

  • ✅ 运营控制力最强,可调整任何样式
  • ✅ 前端实现简单,直接绑定 style
  • ✅ 不需要前端建立变量层

缺点:

  • ❌ 违反关注点分离 — 后端成了 CSS 容器
  • ❌ 设备感知缺失 — 后端无法感知 DPI/屏幕
  • ❌ 配置键膨胀 — N 场景 × M 属性 = N×M 个配置
  • ❌ 暗黑模式翻倍 — 每种颜色两个配置键
  • ❌ 安全风险 — 可能注入恶意 CSS
  • ❌ 维护成本高 — 样式散落在后端代码中

⚠️ 这是当前大多数电商 的现状,也是需要矫正的方案。

典型代码(当前项目):

// 后端:ComponentStyle 镜像 CSS
public class ComponentStyle {
private String color; // CSS color
private String fontSize; // CSS font-size
private String margin; // CSS margin ← 不应下发
private String height; // CSS height ← 不应下发
private String padding; // CSS padding ← 不应下发
private String textOverflow; // CSS text-overflow ← 不应下发
// … 19 个字段
}

{
"_comment": "API 响应 — CSS 属性级下发(反模式示例)",
"style": {
"color": "#FF0F23",
"fontSize": "14",
"margin": "0 0 0 2",
"height": "6",
"textOverflow": "ellipsis"
}
}

矫正方向:向方案二(CSS 变量 + 配置注入)迁移,剥离布局属性,语义化色值。


方案五:模板化渲染

架构:服务端下发模板 + 数据 → 前端模板引擎渲染
动态性:模板可动态切换,样式随模板走
服务端角色:下发模板 ID + 数据

适用场景:

  • 多模板场景(咪咕模板、兑换券模板、政企模板)
  • 模板数量有限但差异大
  • 需要服务端控制楼层顺序和组合

优点:

  • ✅ 模板可动态切换
  • ✅ 样式封装在模板内,不泄漏
  • ✅ 服务端控制楼层编排

缺点:

  • ❌ 模板开发成本高
  • ❌ 模板数量膨胀后难维护
  • ❌ 样式变更仍需发版(模板发版)
  • ❌ 前端模板引擎有性能开销

架构图:

#mermaid-svg-IQEOtH6TT85wDCvT{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-IQEOtH6TT85wDCvT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-IQEOtH6TT85wDCvT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-IQEOtH6TT85wDCvT .error-icon{fill:#552222;}#mermaid-svg-IQEOtH6TT85wDCvT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-IQEOtH6TT85wDCvT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-IQEOtH6TT85wDCvT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-IQEOtH6TT85wDCvT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-IQEOtH6TT85wDCvT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-IQEOtH6TT85wDCvT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-IQEOtH6TT85wDCvT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-IQEOtH6TT85wDCvT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-IQEOtH6TT85wDCvT .marker.cross{stroke:#333333;}#mermaid-svg-IQEOtH6TT85wDCvT svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-IQEOtH6TT85wDCvT p{margin:0;}#mermaid-svg-IQEOtH6TT85wDCvT .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-IQEOtH6TT85wDCvT .cluster-label text{fill:#333;}#mermaid-svg-IQEOtH6TT85wDCvT .cluster-label span{color:#333;}#mermaid-svg-IQEOtH6TT85wDCvT .cluster-label span p{background-color:transparent;}#mermaid-svg-IQEOtH6TT85wDCvT .label text,#mermaid-svg-IQEOtH6TT85wDCvT span{fill:#333;color:#333;}#mermaid-svg-IQEOtH6TT85wDCvT .node rect,#mermaid-svg-IQEOtH6TT85wDCvT .node circle,#mermaid-svg-IQEOtH6TT85wDCvT .node ellipse,#mermaid-svg-IQEOtH6TT85wDCvT .node polygon,#mermaid-svg-IQEOtH6TT85wDCvT .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-IQEOtH6TT85wDCvT .rough-node .label text,#mermaid-svg-IQEOtH6TT85wDCvT .node .label text,#mermaid-svg-IQEOtH6TT85wDCvT .image-shape .label,#mermaid-svg-IQEOtH6TT85wDCvT .icon-shape .label{text-anchor:middle;}#mermaid-svg-IQEOtH6TT85wDCvT .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-IQEOtH6TT85wDCvT .rough-node .label,#mermaid-svg-IQEOtH6TT85wDCvT .node .label,#mermaid-svg-IQEOtH6TT85wDCvT .image-shape .label,#mermaid-svg-IQEOtH6TT85wDCvT .icon-shape .label{text-align:center;}#mermaid-svg-IQEOtH6TT85wDCvT .node.clickable{cursor:pointer;}#mermaid-svg-IQEOtH6TT85wDCvT .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-IQEOtH6TT85wDCvT .arrowheadPath{fill:#333333;}#mermaid-svg-IQEOtH6TT85wDCvT .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-IQEOtH6TT85wDCvT .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-IQEOtH6TT85wDCvT .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IQEOtH6TT85wDCvT .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-IQEOtH6TT85wDCvT .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IQEOtH6TT85wDCvT .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-IQEOtH6TT85wDCvT .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-IQEOtH6TT85wDCvT .cluster text{fill:#333;}#mermaid-svg-IQEOtH6TT85wDCvT .cluster span{color:#333;}#mermaid-svg-IQEOtH6TT85wDCvT div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-IQEOtH6TT85wDCvT .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-IQEOtH6TT85wDCvT rect.text{fill:none;stroke-width:0;}#mermaid-svg-IQEOtH6TT85wDCvT .icon-shape,#mermaid-svg-IQEOtH6TT85wDCvT .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IQEOtH6TT85wDCvT .icon-shape p,#mermaid-svg-IQEOtH6TT85wDCvT .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-IQEOtH6TT85wDCvT .icon-shape .label rect,#mermaid-svg-IQEOtH6TT85wDCvT .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IQEOtH6TT85wDCvT .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-IQEOtH6TT85wDCvT .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-IQEOtH6TT85wDCvT :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

模板ID + 数据

模板仓库模板A / 模板B / 模板C

BF模板选择 + 数据填充

前端模板引擎渲染

页面输出

代码示例:

// 后端:模板选择
public class TemplateVo {
private String templateId; // 模板 ID
private String templateType; // migu / exchangeCoupon / multiplatformP
private List<FloorVO> floorList; // 楼层列表(数据 + 排序)
private Map<String, Object> commonData; // 公共数据
}

// 前端:模板引擎渲染
const templates = {
migu: MiguTemplate,
exchangeCoupon: ExchangeCouponTemplate,
multiplatformP: MultiplatformTemplate,
};

function render(templateType, floorList) {
const Template = templates[templateType];
return <Template floors={floorList} />;
}


方案六:Server-Driven UI(SDUI)

架构:服务端下发完整 UI 指令 → 前端通用渲染器执行
动态性:完全动态,前端零样式代码
服务端角色:下发完整 UI 描述(组件类型 + 属性 + 样式 + 事件)

适用场景:

  • 超级 App 内嵌页面(无需发版即可改 UI)
  • 跨端一致性要求极高
  • 前端团队弱,服务端兜底

优点:

  • ✅ 最大动态性,无需发版即可改任何 UI
  • ✅ 跨端一致性(同一套指令,各端渲染器执行)
  • ✅ 前端代码量极少

缺点:

  • ❌ 服务端复杂度爆炸 — 需要知道所有设备的渲染规则
  • ❌ 性能开销大 — 指令解析 + 渲染比原生慢
  • ❌ 调试困难 — 样式问题需查服务端代码
  • ❌ 设备适配极差 — 服务端无法感知设备上下文
  • ❌ 过度工程 — 大多数场景不需要这种动态性

⚠️ 除非你有充分的理由(如超级 App 的零发版需求),否则不要选择此方案。

架构图:

#mermaid-svg-MWDTD84VhBZBtdz0{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-MWDTD84VhBZBtdz0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-MWDTD84VhBZBtdz0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-MWDTD84VhBZBtdz0 .error-icon{fill:#552222;}#mermaid-svg-MWDTD84VhBZBtdz0 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-MWDTD84VhBZBtdz0 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-MWDTD84VhBZBtdz0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MWDTD84VhBZBtdz0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MWDTD84VhBZBtdz0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-MWDTD84VhBZBtdz0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MWDTD84VhBZBtdz0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MWDTD84VhBZBtdz0 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-MWDTD84VhBZBtdz0 .marker.cross{stroke:#333333;}#mermaid-svg-MWDTD84VhBZBtdz0 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MWDTD84VhBZBtdz0 p{margin:0;}#mermaid-svg-MWDTD84VhBZBtdz0 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-MWDTD84VhBZBtdz0 .cluster-label text{fill:#333;}#mermaid-svg-MWDTD84VhBZBtdz0 .cluster-label span{color:#333;}#mermaid-svg-MWDTD84VhBZBtdz0 .cluster-label span p{background-color:transparent;}#mermaid-svg-MWDTD84VhBZBtdz0 .label text,#mermaid-svg-MWDTD84VhBZBtdz0 span{fill:#333;color:#333;}#mermaid-svg-MWDTD84VhBZBtdz0 .node rect,#mermaid-svg-MWDTD84VhBZBtdz0 .node circle,#mermaid-svg-MWDTD84VhBZBtdz0 .node ellipse,#mermaid-svg-MWDTD84VhBZBtdz0 .node polygon,#mermaid-svg-MWDTD84VhBZBtdz0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-MWDTD84VhBZBtdz0 .rough-node .label text,#mermaid-svg-MWDTD84VhBZBtdz0 .node .label text,#mermaid-svg-MWDTD84VhBZBtdz0 .image-shape .label,#mermaid-svg-MWDTD84VhBZBtdz0 .icon-shape .label{text-anchor:middle;}#mermaid-svg-MWDTD84VhBZBtdz0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-MWDTD84VhBZBtdz0 .rough-node .label,#mermaid-svg-MWDTD84VhBZBtdz0 .node .label,#mermaid-svg-MWDTD84VhBZBtdz0 .image-shape .label,#mermaid-svg-MWDTD84VhBZBtdz0 .icon-shape .label{text-align:center;}#mermaid-svg-MWDTD84VhBZBtdz0 .node.clickable{cursor:pointer;}#mermaid-svg-MWDTD84VhBZBtdz0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-MWDTD84VhBZBtdz0 .arrowheadPath{fill:#333333;}#mermaid-svg-MWDTD84VhBZBtdz0 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-MWDTD84VhBZBtdz0 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-MWDTD84VhBZBtdz0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MWDTD84VhBZBtdz0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-MWDTD84VhBZBtdz0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MWDTD84VhBZBtdz0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-MWDTD84VhBZBtdz0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-MWDTD84VhBZBtdz0 .cluster text{fill:#333;}#mermaid-svg-MWDTD84VhBZBtdz0 .cluster span{color:#333;}#mermaid-svg-MWDTD84VhBZBtdz0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-MWDTD84VhBZBtdz0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-MWDTD84VhBZBtdz0 rect.text{fill:none;stroke-width:0;}#mermaid-svg-MWDTD84VhBZBtdz0 .icon-shape,#mermaid-svg-MWDTD84VhBZBtdz0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MWDTD84VhBZBtdz0 .icon-shape p,#mermaid-svg-MWDTD84VhBZBtdz0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-MWDTD84VhBZBtdz0 .icon-shape .label rect,#mermaid-svg-MWDTD84VhBZBtdz0 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MWDTD84VhBZBtdz0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-MWDTD84VhBZBtdz0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-MWDTD84VhBZBtdz0 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

UI 描述

JSON 指令

JSON 指令

JSON 指令

运营后台拖拽搭建

服务端生成 UI 指令

Web 渲染器

iOS 渲染器

Android 渲染器

指令示例:

{
"type": "Container",
"children": [
{
"type": "Text",
"props": {
"value": "¥99.00",
"style": { "color": "#FF0F23", "fontSize": 18, "fontWeight": "bold" }
}
},
{
"type": "Button",
"props": {
"label": "立即购买",
"style": { "backgroundColor": "#FF0F23", "borderRadius": 20 },
"action": { "type": "addToCart" }
}
}
]
}


四、选型矩阵

4.1 六维对比

维度方案一纯前端方案二CSS变量注入方案三Design Tokens方案四组件级下发方案五模板化方案六SDUI
动态性 ★☆☆☆☆ ★★★☆☆ ★★★☆☆ ★★★★★ ★★★★☆ ★★★★★
前端自主性 ★★★★★ ★★★★☆ ★★★★☆ ★☆☆☆☆ ★★★☆☆ ★☆☆☆☆
设备适配 ★★★★★ ★★★★★ ★★★★★ ★☆☆☆☆ ★★★☆☆ ★☆☆☆☆
维护成本 ★★★★★ ★★★★☆ ★★★☆☆ ★★☆☆☆ ★★☆☆☆ ★☆☆☆☆
改造成本 ★★★★★ ★★★★☆ ★★☆☆☆ ★★★★★ ★★★☆☆ ★☆☆☆☆
跨端一致性 ★★☆☆☆ ★★★☆☆ ★★★★★ ★★☆☆☆ ★★★★☆ ★★★★★

4.2 场景-方案匹配

业务场景推荐方案理由
内容型网站 方案一 无动态需求,简单高效
电商商详页 方案二 色值需动态,布局前端自主
大型设计系统 方案三 多端统一,设计-开发对齐
运营拖拽搭建 方案四→方案二 当前方案四,逐步向方案二迁移
多模板商详 方案二+方案五 令牌管色值,模板管结构
超级 App 方案六 零发版需求,可接受复杂度

4.3 迁移路径

当前大多数电商系统:
方案四(组件级下发)→ 方案二(CSS 变量注入)→ 方案三(Design Tokens)

Phase 1: 剥离布局属性(1-2周)
Phase 2: 语义化色值 + CSS 变量层(2-4周)
Phase 3: 统一暗黑模式(2-3周)
Phase 4: Design Tokens 流水线(4-8周)


五、推荐架构:方案二的详细设计

5.1 整体架构

#mermaid-svg-9V5FeGWH1V8tlqhk{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9V5FeGWH1V8tlqhk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9V5FeGWH1V8tlqhk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9V5FeGWH1V8tlqhk .error-icon{fill:#552222;}#mermaid-svg-9V5FeGWH1V8tlqhk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9V5FeGWH1V8tlqhk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9V5FeGWH1V8tlqhk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9V5FeGWH1V8tlqhk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9V5FeGWH1V8tlqhk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9V5FeGWH1V8tlqhk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9V5FeGWH1V8tlqhk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9V5FeGWH1V8tlqhk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9V5FeGWH1V8tlqhk .marker.cross{stroke:#333333;}#mermaid-svg-9V5FeGWH1V8tlqhk svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9V5FeGWH1V8tlqhk p{margin:0;}#mermaid-svg-9V5FeGWH1V8tlqhk .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-9V5FeGWH1V8tlqhk .cluster-label text{fill:#333;}#mermaid-svg-9V5FeGWH1V8tlqhk .cluster-label span{color:#333;}#mermaid-svg-9V5FeGWH1V8tlqhk .cluster-label span p{background-color:transparent;}#mermaid-svg-9V5FeGWH1V8tlqhk .label text,#mermaid-svg-9V5FeGWH1V8tlqhk span{fill:#333;color:#333;}#mermaid-svg-9V5FeGWH1V8tlqhk .node rect,#mermaid-svg-9V5FeGWH1V8tlqhk .node circle,#mermaid-svg-9V5FeGWH1V8tlqhk .node ellipse,#mermaid-svg-9V5FeGWH1V8tlqhk .node polygon,#mermaid-svg-9V5FeGWH1V8tlqhk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9V5FeGWH1V8tlqhk .rough-node .label text,#mermaid-svg-9V5FeGWH1V8tlqhk .node .label text,#mermaid-svg-9V5FeGWH1V8tlqhk .image-shape .label,#mermaid-svg-9V5FeGWH1V8tlqhk .icon-shape .label{text-anchor:middle;}#mermaid-svg-9V5FeGWH1V8tlqhk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-9V5FeGWH1V8tlqhk .rough-node .label,#mermaid-svg-9V5FeGWH1V8tlqhk .node .label,#mermaid-svg-9V5FeGWH1V8tlqhk .image-shape .label,#mermaid-svg-9V5FeGWH1V8tlqhk .icon-shape .label{text-align:center;}#mermaid-svg-9V5FeGWH1V8tlqhk .node.clickable{cursor:pointer;}#mermaid-svg-9V5FeGWH1V8tlqhk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-9V5FeGWH1V8tlqhk .arrowheadPath{fill:#333333;}#mermaid-svg-9V5FeGWH1V8tlqhk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9V5FeGWH1V8tlqhk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9V5FeGWH1V8tlqhk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9V5FeGWH1V8tlqhk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9V5FeGWH1V8tlqhk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9V5FeGWH1V8tlqhk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-9V5FeGWH1V8tlqhk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9V5FeGWH1V8tlqhk .cluster text{fill:#333;}#mermaid-svg-9V5FeGWH1V8tlqhk .cluster span{color:#333;}#mermaid-svg-9V5FeGWH1V8tlqhk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-9V5FeGWH1V8tlqhk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9V5FeGWH1V8tlqhk rect.text{fill:none;stroke-width:0;}#mermaid-svg-9V5FeGWH1V8tlqhk .icon-shape,#mermaid-svg-9V5FeGWH1V8tlqhk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9V5FeGWH1V8tlqhk .icon-shape p,#mermaid-svg-9V5FeGWH1V8tlqhk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-9V5FeGWH1V8tlqhk .icon-shape .label rect,#mermaid-svg-9V5FeGWH1V8tlqhk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9V5FeGWH1V8tlqhk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9V5FeGWH1V8tlqhk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9V5FeGWH1V8tlqhk :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

前端

BF 层

配置中心

设计系统

定义色值/刻度

同步

策略匹配按用户/版本/渠道

注入

响应

解析

Figma / 设计规范

令牌定义token-definitions.json

duc / Apollo / Nacos

令牌值集tokenSet + tokenSetDark

API 接口

JSON 响应{tokenSet, tokenSetDark, floorList}

JS 注入层tokenSet → CSS 变量

CSS 变量层:root { –token: value }

暗黑层@media dark

组件层var(–token)

布局层Flexbox/Grid

动画层transition/keyframes

适配层DPI/安全区域/响应式

5.2 接口规范

后端响应结构

public class PageResponse {
// 令牌区 — 服务端下发设计决策
private Map<String, String> tokenSet; // 亮色令牌集(必填)
private Map<String, String> tokenSetDark; // 暗色令牌集(可选)

// 数据区 — 业务数据
private List<FloorVO> floorList; // 楼层列表
private List<PopLayerVO> popLayerVOS; // 弹层列表
private Map<String, Object> commonData; // 公共数据

// 模板区 — 模板选择
private TemplateModel templateModel; // 模板信息

// ❌ 不再下发:ComponentStyle 中的 margin/padding/height/width/lineHeight/textOverflow 等
}

令牌命名规范
令牌类别命名格式示例
品牌色 brand-{语义} brand-primary, brand-secondary
业务色 {业务}-{语义}-color price-main-color, tag-danger-color
语义色 color-{语义} color-success, color-warning, color-error
间距刻度 spacing-{尺寸} spacing-xs, spacing-sm, spacing-md, spacing-lg
圆角刻度 radius-{尺寸} radius-sm, radius-md, radius-lg
字号刻度 font-size-{语义} font-size-title, font-size-body, font-size-caption
字重刻度 font-weight-{语义} font-weight-regular, font-weight-bold

5.3 前端分层架构

#mermaid-svg-AufhkuKrSfpbwSvZ{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-AufhkuKrSfpbwSvZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-AufhkuKrSfpbwSvZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-AufhkuKrSfpbwSvZ .error-icon{fill:#552222;}#mermaid-svg-AufhkuKrSfpbwSvZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-AufhkuKrSfpbwSvZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-AufhkuKrSfpbwSvZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-AufhkuKrSfpbwSvZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-AufhkuKrSfpbwSvZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-AufhkuKrSfpbwSvZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-AufhkuKrSfpbwSvZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-AufhkuKrSfpbwSvZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-AufhkuKrSfpbwSvZ .marker.cross{stroke:#333333;}#mermaid-svg-AufhkuKrSfpbwSvZ svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-AufhkuKrSfpbwSvZ p{margin:0;}#mermaid-svg-AufhkuKrSfpbwSvZ .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-AufhkuKrSfpbwSvZ .cluster-label text{fill:#333;}#mermaid-svg-AufhkuKrSfpbwSvZ .cluster-label span{color:#333;}#mermaid-svg-AufhkuKrSfpbwSvZ .cluster-label span p{background-color:transparent;}#mermaid-svg-AufhkuKrSfpbwSvZ .label text,#mermaid-svg-AufhkuKrSfpbwSvZ span{fill:#333;color:#333;}#mermaid-svg-AufhkuKrSfpbwSvZ .node rect,#mermaid-svg-AufhkuKrSfpbwSvZ .node circle,#mermaid-svg-AufhkuKrSfpbwSvZ .node ellipse,#mermaid-svg-AufhkuKrSfpbwSvZ .node polygon,#mermaid-svg-AufhkuKrSfpbwSvZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-AufhkuKrSfpbwSvZ .rough-node .label text,#mermaid-svg-AufhkuKrSfpbwSvZ .node .label text,#mermaid-svg-AufhkuKrSfpbwSvZ .image-shape .label,#mermaid-svg-AufhkuKrSfpbwSvZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-AufhkuKrSfpbwSvZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-AufhkuKrSfpbwSvZ .rough-node .label,#mermaid-svg-AufhkuKrSfpbwSvZ .node .label,#mermaid-svg-AufhkuKrSfpbwSvZ .image-shape .label,#mermaid-svg-AufhkuKrSfpbwSvZ .icon-shape .label{text-align:center;}#mermaid-svg-AufhkuKrSfpbwSvZ .node.clickable{cursor:pointer;}#mermaid-svg-AufhkuKrSfpbwSvZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-AufhkuKrSfpbwSvZ .arrowheadPath{fill:#333333;}#mermaid-svg-AufhkuKrSfpbwSvZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-AufhkuKrSfpbwSvZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-AufhkuKrSfpbwSvZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AufhkuKrSfpbwSvZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-AufhkuKrSfpbwSvZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AufhkuKrSfpbwSvZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-AufhkuKrSfpbwSvZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-AufhkuKrSfpbwSvZ .cluster text{fill:#333;}#mermaid-svg-AufhkuKrSfpbwSvZ .cluster span{color:#333;}#mermaid-svg-AufhkuKrSfpbwSvZ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-AufhkuKrSfpbwSvZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-AufhkuKrSfpbwSvZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-AufhkuKrSfpbwSvZ .icon-shape,#mermaid-svg-AufhkuKrSfpbwSvZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AufhkuKrSfpbwSvZ .icon-shape p,#mermaid-svg-AufhkuKrSfpbwSvZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-AufhkuKrSfpbwSvZ .icon-shape .label rect,#mermaid-svg-AufhkuKrSfpbwSvZ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AufhkuKrSfpbwSvZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-AufhkuKrSfpbwSvZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-AufhkuKrSfpbwSvZ :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

Layer 4: 布局/动画/适配层

Flexbox/Grid 布局

transition/animation

@media 响应式

safe-area-inset 适配

Layer 3: 组件样式层

.price-label { color: var(–price-main) }

.card { border-radius: var(–radius-md) }

Layer 2: CSS 变量层

:root { –price-main: #FF0F23 }

@media dark { :root { –price-main: #FF6B6B } }

Layer 1: 令牌注入层

解析 tokenSet

document.documentElement.setProperty

注入 @media dark 规则


六、暗黑模式架构对比

6.1 三种暗黑模式方案

方案实现配置键数量后端复杂度前端复杂度扩展性
方案 A:双配置键 每种颜色两个键 2N
方案 B:令牌集 tokenSet + tokenSetDark N
方案 C:前端主题 后端不下发暗色,前端自己算 0 最好

6.2 推荐方案 B

后端下发:
tokenSet: { "price-main-color": "#FF0F23" } // 亮色
tokenSetDark: { "price-main-color": "#FF6B6B" } // 暗色(仅差异项)

前端消费:
:root { –price-main-color: #FF0F23; } // JS 注入 tokenSet
@media (prefers-color-scheme: dark) {
:root { –price-main-color: #FF6B6B; } // JS 注入 tokenSetDark
}
.price-label { color: var(–price-main-color); } // 组件不感知主题

优势:

  • 令牌名稳定,组件代码零条件分支
  • 新增主题模式(如高对比度)只需扩展 tokenSet,后端无需改动
  • 暗色值由设计团队定义,前端不需要"猜"

七、反模式与自检

7.1 十大反模式

#反模式危害等级矫正
1 后端镜像 CSS(VO 字段 = CSS 属性) 🔴 高 用令牌替代
2 后端硬编码像素值 🔴 高 像素值归前端
3 配置键翻倍(暗黑双键) 🟡 中 令牌集 + 前端主题
4 色值散落多个 Java 类 🟡 中 统一到令牌系统
5 下发布局属性(margin/padding/height) 🔴 高 布局归前端
6 下发 CSS 规则(textOverflow/fontFamily) 🟡 中 规则归前端
7 前端硬编码色值 🟡 中 引用 CSS 变量
8 下发完整 CSS 字符串 🔴 高 下发结构化令牌
9 暗黑模式 filter:invert(1) 🟡 中 令牌级主题切换
10 配置键散落业务代码 🟢 低 集中常量类管理

7.2 五步自检法

面对一个样式属性,依次回答:

  • 运营改? → 需要 → 继续问 2
  • 改语义? → 改变 → 继续问 3
  • 设备关? → 无关 → 继续问 4
  • 值或规? → 值 → 🔴 服务端下发令牌
  • 令牌否? → 令牌名+值 → ✅ 合规
  • 任何一步跳到"否":🟢 前端自主。


    八、名词解释

    术语释义
    Design Tokens W3C 社区组标准化的设计决策载体
    Style Dictionary Amazon 开源的设计令牌构建系统
    CSS 变量 –var: value,前端原生主题机制
    语义令牌 表达意图的令牌(如 price-main-color)
    令牌集 一组语义令牌的集合,可按主题区分
    样式下沉 CSS 属性从前端移至后端
    SDUI Server-Driven UI,服务端驱动 UI
    BF Backend for Frontend
    关注点分离 每个模块只负责一个关注点

    九、权威资料

    资料说明
    W3C Design Tokens Format https://design-tokens.github.io/community-group/format/
    W3C CSS Custom Properties https://www.w3.org/TR/css-variables-1/
    Style Dictionary https://amzn.github.io/style-dictionary/
    Theo (Salesforce) https://github.com/salesforce-ux/theo
    Figma Tokens https://github.com/tokens-studio/figma-plugin
    Sam Newman — BFF Pattern Building Microservices, O’Reilly
    Martin Fowler — Feature Toggles https://martinfowler.com/articles/feature-toggles.html
    Nathan Curtis — Design Systems API Design Systems API 设计

    十、速记口诀

    「值可下,规不下,令牌桥,主题解」

    • 值可下:色值、刻度、URL、文案 → 服务端下发
    • 规不下:布局、动画、排版、适配 → 前端自主
    • 令牌桥:语义令牌桥接后端值和前端规则
    • 主题解:暗黑模式前端解耦,不靠配置翻倍

    「运营改?设备关?值或规?」

  • 运营改 → 下发值
  • 设备关 → 前端写
  • 值 → 可下发;规 → 不下发
  • 「后端不写 CSS,前端不写色值」


    本文为通用技术架构方案,适用于电商及类似动态化系统的前后端样式治理选型与设计。

    赞(0)
    未经允许不得转载:171主机测评 » 前端样式架构全景:场景分类、技术方案与选型指南
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址