Props 动态样式与 CSS 变量
两边都可以把运行时数据映射到 CSS 自定义属性,差异集中在状态与 style 绑定语法。
核心结论
当样式值来自运行时数据时,可以把少量动态值放进 CSS 自定义属性,把布局、交互态和响应式规则继续留在 CSS 中。React 与 Vue 的主要差异只是 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:#fffdf8,stroke:#149eca,color:#25221d,stroke-width:1.2px
classDef component fill:#e4f5fa,stroke:#149eca,color:#25221d,stroke-width:2px,font-weight:600
classDef process fill:#f4fafb,stroke:#149eca,color:#25221d,stroke-width:1.6px
classDef resource fill:#eee9df,stroke:#817a70,color:#25221d,stroke-width:1.4pxsetter 触发组件重新执行,并在本次 render 中创建新的 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:#fffdf8,stroke:#2f9d6b,color:#25221d,stroke-width:1.2px
classDef component fill:#e4f4eb,stroke:#2f9d6b,color:#25221d,stroke-width:2px,font-weight:600
classDef process fill:#f2f8f4,stroke:#2f9d6b,color:#25221d,stroke-width:1.6px
classDef resource fill:#eee9df,stroke:#817a70,color:#25221d,stroke-width:1.4px响应式 ref 使两个 computed 按依赖失效,并在模板读取时复用缓存。
完整 Demo 源码
以下代码直接读取在线 Demo 使用的源文件,没有省略或改写。
React 源码
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 源码
<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>在线观察
切换主题会同时改变强调色、背景与圆角,CSS 结构保持不变。
React
运行时主题
颜色与圆角都来自组件状态。
Vue 3
运行时主题
颜色与圆角都来自组件状态。
选择规则
- 离散且有语义的视觉状态优先使用 class,如
is-active。 - 连续数值、用户颜色或设计令牌适合 CSS 变量。
- 不要把所有静态 CSS 都搬进 style 对象;伪类、媒体查询和级联仍应由 CSS 表达。
- 动态值进入
url()等敏感位置前需要验证,避免把任意输入直接拼入样式。
组件级样式隔离见CSS Modules 与 scoped。