跳转到内容

Props 动态样式与 CSS 变量

切换主题会同时改变强调色、背景与圆角。Demo 的 CSS 结构一致,只对比如何从组件状态生成自定义属性。

React

扩展 CSSProperties 类型后,把变量传给 style。

在线预览

海蓝

运行时主题

颜色与圆角都来自组件状态。

组件关系与数据流

StateComponentDerivedStyle

差异焦点动态样式对象属于 render 结果,每次组件执行都会重新创建。

实线表示依赖、数据或命令传递,虚线表示事件、回调或清理路径。

flowchart TB
  accTitle: React render 创建动态 style
  accDescr: themeIndex setter 触发 ReactDemo 重新执行,每次 render 都读取 theme 并创建新的 style 对象,再通过 style prop 写入 CSS 变量。
  state(["themeIndex state"]) --> render["ReactDemo 重新执行"]
  render --> derive{{"每次 render 创建 style 对象"}}
  derive --> variables[("CSS variables")]
  variables -->|"style prop"| card["dynamic-demo-card"]
  card -.->|"setThemeIndex"| state
  class state state
  class render,card component
  class derive process
  class variables resource
  classDef state fill:#ffffff,stroke:#149eca,color:#172033,stroke-width:1.2px
  classDef component fill:#e5f6fb,stroke:#149eca,color:#172033,stroke-width:2px,font-weight:600
  classDef process fill:#f4fbfd,stroke:#149eca,color:#172033,stroke-width:1.6px
  classDef resource fill:#f3f5f8,stroke:#778195,color:#172033,stroke-width:1.4px

setter 触发组件重新执行,并在本次 render 中创建新的 style 对象。

React 流程图
React 组件关系与数据流

滚轮缩放 · 按住拖动 · 双击重置 · Esc 关闭

实际运行源码

import { useState, type CSSProperties } from "react";
const themes = [
{ name: "海蓝", color: "#1677ff", background: "#eaf3ff", radius: 18 },
{ name: "葡萄", color: "#7c3aed", background: "#f4edff", radius: 8 },
];
type CardStyle = CSSProperties & {
"--demo-accent": string;
"--demo-background": string;
"--demo-radius": string;
};
export function ReactDemo() {
const [themeIndex, setThemeIndex] = useState(0);
const theme = themes[themeIndex];
const style: CardStyle = {
"--demo-accent": theme.color,
"--demo-background": theme.background,
"--demo-radius": `${theme.radius}px`,
};
return (
<div className="demo-stack">
<section className="dynamic-demo-card" style={style}>
<span className="demo-tag">{theme.name}</span>
<h2>运行时主题</h2>
<p>颜色与圆角都来自组件状态。</p>
</section>
<div className="demo-actions">
{themes.map((item, index) => (
<button
className="demo-button"
type="button"
key={item.name}
onClick={() => setThemeIndex(index)}
>
{item.name}
</button>
))}
</div>
</div>
);
}
Vue 3

用 computed 生成样式对象,再通过 :style 绑定。

在线预览

海蓝

运行时主题

颜色与圆角都来自组件状态。

组件关系与数据流

StateComponentDerivedStyle

差异焦点computed 自动追踪 themeIndex,并缓存 theme 与 cardStyle。

实线表示依赖、数据或命令传递,虚线表示事件、回调或清理路径。

flowchart TB
  accTitle: Vue computed 动态 style
  accDescr: themeIndex ref 变化只使依赖它的 theme 和 cardStyle computed 失效,模板读取 cardStyle 时得到缓存后的 CSS 变量对象。
  state(["themeIndex ref"]) --> theme{{"computed theme"}}
  theme --> cardStyleNode{{"computed cardStyle"}}
  cardStyleNode --> variables[("缓存的 CSS variables")]
  variables -->|":style binding"| card["dynamic-demo-card"]
  card -.->|"直接修改 themeIndex"| state
  class state state
  class card component
  class theme,cardStyleNode process
  class variables resource
  classDef state fill:#ffffff,stroke:#42b883,color:#172033,stroke-width:1.2px
  classDef component fill:#e7f8f0,stroke:#42b883,color:#172033,stroke-width:2px,font-weight:600
  classDef process fill:#f2fbf7,stroke:#42b883,color:#172033,stroke-width:1.6px
  classDef resource fill:#f3f5f8,stroke:#778195,color:#172033,stroke-width:1.4px

响应式 ref 使两个 computed 按依赖失效,并在模板读取时复用缓存。

Vue 3 流程图
Vue 3 组件关系与数据流

滚轮缩放 · 按住拖动 · 双击重置 · Esc 关闭

实际运行源码

<script setup lang="ts">
import { computed, ref } from "vue";
const themes = [
{ name: "海蓝", color: "#1677ff", background: "#eaf3ff", radius: 18 },
{ name: "葡萄", color: "#7c3aed", background: "#f4edff", radius: 8 },
];
const themeIndex = ref(0);
const theme = computed(() => themes[themeIndex.value]);
const cardStyle = computed(() => ({
"--demo-accent": theme.value.color,
"--demo-background": theme.value.background,
"--demo-radius": `${theme.value.radius}px`,
}));
</script>
<template>
<div class="demo-stack">
<section class="dynamic-demo-card" :style="cardStyle">
<span class="demo-tag">{{ theme.name }}</span>
<h2>运行时主题</h2>
<p>颜色与圆角都来自组件状态。</p>
</section>
<div class="demo-actions">
<button
v-for="(item, index) in themes"
:key="item.name"
class="demo-button"
type="button"
@click="themeIndex = index"
>
{{ item.name }}
</button>
</div>
</div>
</template>