惰性求值在前端里到底该怎么用才对?

___晨晰 阅读 27

最近在重构一个数据表格组件,发现每次滚动都要重新计算大量行数据,性能很差。听说可以用惰性求值优化,就试着把计算逻辑包进 getter 里,比如 get formattedData(),但好像没起作用——每次 render 还是全量跑。

我是不是理解错了?惰性求值在 JS 里是不是得配合 Proxy 或者 memoize 才行?比如下面这种写法:

const createLazyValue = (fn) => {
  let cached = false;
  let result;
  return () => {
    if (!cached) {
      result = fn();
      cached = true;
    }
    return result;
  };
};

但这样又没法响应数据变化……有没有更合适的实践方式?

我来解答 赞 6 收藏
二维码
手机扫码查看
2 条解答
皇甫柯豫
你理解的方向是对的,不过惰性求值在前端确实有点讲究。先说你的问题,把计算逻辑包进 getter 里其实不够,因为每次 render 都会触发 getter,等于没优化。

JS里面要实现真正的惰性求值,确实得配合点别的东西。你说的 createLazyValue 方案思路没错,但确实解决不了数据变化的问题。我给你个更实际的方案:

class LazyData {
constructor(fn) {
this.fn = fn;
this.value = null;
this.dirty = true;
}

get() {
if (this.dirty) {
this.value = this.fn();
this.dirty = false;
}
return this.value;
}

invalidate() {
this.dirty = true;
}
}

// 使用示例
const lazyData = new LazyData(() => {
// 模拟耗时计算
console.log('计算中...');
let result = [];
for (let i = 0; i < 10000; i++) {
result.push(i * 2);
}
return result;
});

console.log(lazyData.get()); // 只会计算一次
console.log(lazyData.get()); // 直接返回缓存结果

// 数据更新时调用invalidate
lazyData.invalidate();
console.log(lazyData.get()); // 再次计算


这个方案的好处是:只有当数据真正需要更新时才重新计算,而且结构清晰。你可以根据组件的状态管理机制,在适当时候调用 invalidate 方法标记数据失效。

如果要用在 React 组件里,可以在 useEffect 里监听相关依赖的变化,然后调用 invalidate。这样既保持了性能优化,又能响应数据变化。记住别过度优化,有时候简单反而更好。
点赞
2026-03-31 23:22
程序猿晓莉
你的问题很典型,很多开发者在处理大量数据渲染时都会遇到类似困扰。惰性求值确实能帮上忙,但要结合具体场景来用。

先说根本原因:你把计算逻辑放进 getter 里确实是个好思路,但每次 render 都触发 getter 调用,这相当于没做到真正的“惰性”。我们需要更精细化的控制。

这里有个改进方案,把你的 createLazyValue 再优化一下,加上对数据变化的监听能力:

class LazyCalculator {
constructor(computeFn) {
this.computeFn = computeFn;
this.cachedValue = undefined;
this.dirty = true; // 标记是否需要重新计算
}

getValue(inputs) {
// 如果输入有变化或者初次调用,就重新计算
if (this.dirty || JSON.stringify(inputs) !== this.lastInputs) {
this.cachedValue = this.computeFn(inputs);
this.lastInputs = JSON.stringify(inputs);
this.dirty = false;
}
return this.cachedValue;
}

invalidate() {
// 当外部知道数据可能变化时,标记为脏
this.dirty = true;
}
}


这个版本的关键在于增加了 dirty 标志位和对输入参数的追踪。原理是只有当输入数据变化时才重新计算。

举个实际用法:
const calculator = new LazyCalculator((data) => {
// 这里放你的复杂计算逻辑
console.log('计算执行');
return data.map(item => item * 2);
});

// 第一次调用会触发计算
console.log(calculator.getValue([1, 2, 3]));

// 同样的输入不会再计算
console.log(calculator.getValue([1, 2, 3]));

// 输入改变时会重新计算
console.log(calculator.getValue([4, 5, 6]));


如果表格数据更新了,记得调用 calculator.invalidate() 来标记需要重新计算。

不过要注意,这种方案也有代价:JSON.stringify 的性能开销不小,对于特别大数据集可能需要优化比较方法。这也是为啥我一直强调要根据具体场景调整方案——没有银弹啊,兄弟。
点赞
2026-03-26 00:00