用类包裹函数组件是否有弊端?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
相关产品推荐
相关产品推荐

