You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

用类包裹函数组件是否有弊端?DOM优化及memo使用疑问

关于自定义类包裹函数组件的DOM优化问题

一、这种实现方式的DOM优化弊端

每次NumberInput函数组件触发渲染时,都会执行以下操作:

  • 新建一个$NumberInput实例
  • 将实例的render方法赋值给全新的Number变量

在Preact/React的渲染机制中,组件类型的引用变化会被判定为完全不同的组件。这意味着每次渲染时,旧的<input>元素会被卸载销毁,然后重新创建一个新的<input>元素,而不是对现有元素做属性更新。

这种行为会带来两个核心问题:

  • 不必要的DOM销毁与重建,增加浏览器性能开销,在频繁渲染的场景下尤为明显
  • 丢失<input>的临时状态,比如输入焦点、选中的文本范围等,破坏用户体验

二、是否需要对const Number使用memo?

不需要,而且memo在这里完全起不到优化作用。

原因是:每次渲染时Number都是一个全新的函数引用(来自新实例的render方法),memo无法识别这是同一个组件逻辑——它只会对比组件的引用是否一致,而每次的Number都是新的,所以memo会直接跳过缓存,依然触发完全卸载重建的流程,反而多了一层无意义的判断开销。

三、优化方向

如果想保留类封装逻辑的写法,可以用useMemo缓存$NumberInput实例,确保只有当依赖的props变化时才重新创建实例:

/** @jsx h */
import { h, useMemo } from "preact";

class $NumberInput {
  constructor(
    public value: number,
    public onChange: (value: number) => void
  ) {}

  get proceessValue() {
    if (this.value === 1) return 2;
    return this.value;
  }

  render() {
    return <input type="number" value={this.proceessValue} onChange={(e) => this.onChange(Number(e.target.value))} />;
  }
}

function NumberInput(props: { value: number, onChange: (value: number) => void }) {
  // 仅当props.value或onChange变化时,才重新创建实例
  const inputInstance = useMemo(() => new $NumberInput(props.value, props.onChange), [props.value, props.onChange]);
  return inputInstance.render();
}

更推荐的方式是直接遵循函数组件的写法,用useMemo处理需要缓存的计算逻辑,避免额外的类实例化开销:

/** @jsx h */
import { h, useMemo } from "preact";

function NumberInput(props: { value: number, onChange: (value: number) => void }) {
  // 缓存处理后的值,仅当props.value变化时重新计算
  const processedValue = useMemo(() => {
    return props.value === 1 ? 2 : props.value;
  }, [props.value]);

  return <input type="number" value={processedValue} onChange={(e) => props.onChange(Number(e.target.value))} />;
}

内容的提问来源于stack exchange,提问作者ThomasReggi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 20:55:20