跳转到内容

派生状态与 computed

输入搜索词并切换“仅显示有库存”,两个 Demo 都从原始商品与筛选条件推导可见列表,不额外同步一份列表状态。

React

每次渲染时直接执行 filter 得到派生结果。

在线预览

  • Apple$1 · 有库存
  • Dragonfruit$1 · 有库存
  • Passionfruit$2 · 缺货
  • Spinach$2 · 有库存

组件关系与数据流

StateComponentDerivedData

差异焦点没有缓存或订阅关系,派生计算跟随每次组件 render 执行。

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

flowchart TB
  accTitle: React 渲染期间直接派生
  accDescr: query 或 inStockOnly 更新后 ReactDemo 函数重新执行,products filter 在每次 render 中直接运行并生成普通 visibleProducts 值。
  filters(["query + inStockOnly state"]) --> render["ReactDemo 再次执行"]
  products[("products")] --> render
  render --> derive{{"每次 render 执行 filter"}}
  derive --> result[("普通 visibleProducts 值")]
  result --> list["商品列表 UI"]
  class filters state
  class render,list component
  class derive process
  class products,result 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

组件重新执行时直接 filter;visibleProducts 是本次 render 的普通值。

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

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

实际运行源码

import { useState } from "react";
const products = [
{ id: 1, name: "Apple", price: "$1", stocked: true },
{ id: 2, name: "Dragonfruit", price: "$1", stocked: true },
{ id: 3, name: "Passionfruit", price: "$2", stocked: false },
{ id: 4, name: "Spinach", price: "$2", stocked: true },
];
export function ReactDemo() {
const [query, setQuery] = useState("");
const [inStockOnly, setInStockOnly] = useState(false);
const visibleProducts = products.filter((product) => {
const matchesQuery = product.name
.toLowerCase()
.includes(query.toLowerCase());
return matchesQuery && (!inStockOnly || product.stocked);
});
return (
<div className="demo-stack">
<label className="demo-field">
搜索商品
<input
className="demo-input"
value={query}
placeholder="例如 Apple"
onChange={(event) => setQuery(event.target.value)}
/>
</label>
<label className="demo-field">
<span>
<input
type="checkbox"
checked={inStockOnly}
onChange={(event) => setInStockOnly(event.target.checked)}
/>{" "}
仅显示有库存
</span>
</label>
<ul className="demo-list">
{visibleProducts.map((product) => (
<li key={product.id}>
<span>{product.name}</span>
<span className={product.stocked ? "" : "demo-muted"}>
{product.price} · {product.stocked ? "有库存" : "缺货"}
</span>
</li>
))}
</ul>
</div>
);
}
Vue 3

computed 追踪依赖,并缓存派生结果。

在线预览

  • Apple$1 · 有库存
  • Dragonfruit$1 · 有库存
  • Passionfruit$2 · 缺货
  • Spinach$2 · 有库存

组件关系与数据流

StateComponentDerivedData

差异焦点computed 是可缓存的响应式派生值,不依赖组件整体重新执行。

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

flowchart TB
  accTitle: Vue computed 响应式派生
  accDescr: computed 首次读取时自动收集 query 与 inStockOnly refs,依赖未变时返回缓存,依赖变化后重新计算 visibleProducts 并更新列表。
  filters(["query + inStockOnly refs"]) --> tracking{{"computed 自动收集依赖"}}
  products[("products")] --> tracking
  tracking --> cache[("缓存 visibleProducts")]
  cache -->|"依赖未变直接复用"| list["商品列表 UI"]
  class filters state
  class list component
  class tracking process
  class products,cache 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

computed 自动追踪响应式读取,并在依赖未变化时复用缓存。

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

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

实际运行源码

<script setup lang="ts">
import { computed, ref } from "vue";
const products = [
{ id: 1, name: "Apple", price: "$1", stocked: true },
{ id: 2, name: "Dragonfruit", price: "$1", stocked: true },
{ id: 3, name: "Passionfruit", price: "$2", stocked: false },
{ id: 4, name: "Spinach", price: "$2", stocked: true },
];
const query = ref("");
const inStockOnly = ref(false);
const visibleProducts = computed(() =>
products.filter((product) => {
const matchesQuery = product.name
.toLowerCase()
.includes(query.value.toLowerCase());
return matchesQuery && (!inStockOnly.value || product.stocked);
}),
);
</script>
<template>
<div class="demo-stack">
<label class="demo-field">
搜索商品
<input
v-model="query"
class="demo-input"
placeholder="例如 Apple"
/>
</label>
<label class="demo-field">
<span>
<input v-model="inStockOnly" type="checkbox" />
仅显示有库存
</span>
</label>
<ul class="demo-list">
<li v-for="product in visibleProducts" :key="product.id">
<span>{{ product.name }}</span>
<span :class="{ 'demo-muted': !product.stocked }">
{{ product.price }} · {{ product.stocked ? "有库存" : "缺货" }}
</span>
</li>
</ul>
</div>
</template>