Memo

缓存渲染组件,仅在监听的依赖项变化时重新渲染。

API 参考

语法

<Memo watch={watchSource} deep={boolean?}>
  {(value) => ReactNode}
</Memo>

属性

  • watch: 要监听的数据源(store、函数或任何值)
  • deep (可选): 启用深度相等检查以处理嵌套对象
  • children: 接收监听值的渲染函数

示例

昂贵组件优化

import { createStore, Memo } from "zenbox";

const dataStore = createStore({
  items: Array.from({ length: 1000 }, (_, i) => ({
    id: i,
    value: Math.random(),
    name: `Item ${i}`,
  })),
  threshold: 0.5,
});

function ExpensiveVisualization() {
  return (
    <div>
      <h2>数据可视化</h2>

      <Memo
        watch={() => ({
          items: dataStore.value.items,
          threshold: dataStore.value.threshold,
        })}
      >
        {({ items, threshold }) => {
          console.log("渲染昂贵可视化..."); // 仅在依赖项变化时输出

          // 昂贵计算
          const processedItems = items
            .filter((item) => item.value > threshold)
            .map((item) => ({
              ...item,
              processed: Math.pow(item.value, 2) * Math.sin(item.id),
            }))
            .sort((a, b) => b.processed - a.processed);

          return (
            <div className="expensive-chart">
              <h3>处理后的项目 ({processedItems.length})</h3>
              {processedItems.slice(0, 20).map((item) => (
                <div key={item.id} className="chart-item">
                  {item.name}: {item.processed.toFixed(4)}
                </div>
              ))}
            </div>
          );
        }}
      </Memo>
    </div>
  );
}

多 Store 缓存渲染

const cartStore = createStore({
  items: [
    { id: 1, name: "电脑", price: 999, quantity: 1, category: "电子产品" },
    { id: 2, name: "鼠标", price: 25, quantity: 2, category: "电子产品" },
  ],
});

const settingsStore = createStore({
  currency: "¥",
  taxRate: 0.08,
  discountPercent: 0.1,
});

function CartSummary() {
  return (
    <Memo
      watch={() => ({
        items: cartStore.value.items,
        settings: settingsStore.value,
      })}
    >
      {({ items, settings }) => {
        console.log("渲染复杂购物车摘要..."); // 仅在依赖项变化时输出

        // 复杂计算
        const subtotal = items.reduce(
          (sum, item) => sum + item.price * item.quantity,
          0
        );
        const discount = subtotal * settings.discountPercent;
        const tax = (subtotal - discount) * settings.taxRate;
        const total = subtotal - discount + tax;

        return (
          <div className="cart-summary">
            <div className="cart-items">
              {items.map((item) => (
                <div key={item.id} className="cart-item">
                  <span>{item.name}</span>
                  <span>
                    {item.quantity} × {settings.currency} {item.price}
                  </span>
                </div>
              ))}
            </div>

            <div className="cart-totals">
              <div>
                小计: {settings.currency} {subtotal.toFixed(2)}
              </div>
              <div>
                折扣: -{settings.currency} {discount.toFixed(2)}
              </div>
              <div>
                税费: {settings.currency} {tax.toFixed(2)}
              </div>
              <div>
                <strong>
                  总计: {settings.currency} {total.toFixed(2)}
                </strong>
              </div>
            </div>
          </div>
        );
      }}
    </Memo>
  );
}

最佳实践

用于昂贵计算 - 适合包含大量计算的组件

// Good - 昂贵计算受益于缓存
<Memo watch={() => dataStore.value}>
  {(data) => {
    const expensiveResult = heavyComputation(data);
    return <ComplexChart data={expensiveResult} />;
  }}
</Memo>

不要用于频繁变化的数据

// Bad - 计数器频繁变化,使用缓存帮助不大
<Memo watch={() => counterStore.value.count}>
  {(count) => <div>{count}</div>}
</Memo>

明确依赖项 - 只监听影响渲染输出的数据

// Good - 明确依赖项
<Memo
  watch={() => ({ items: store.value.items, threshold: store.value.threshold })}
>
  {({ items, threshold }) => (
    <ProcessedList items={items} threshold={threshold} />
  )}
</Memo>

何时使用 Memo vs 其他选项

使用场景推荐方式原因
昂贵计算<Memo>缓存避免重复计算
简单 store 访问usePickuseStoreValue简单情况下开销更少
副作用useWatchuseWatchEffectMemo 用于渲染,不适合副作用
频繁变化的数据普通组件数据频繁变化时缓存无效

相关文档