performance - React renderToString() 性能和缓存 React 组件

标签 performance reactjs isomorphic-javascript render-to-string react-dom

我注意到reactDOM.renderToString()在服务器上渲染大型组件树时,方法开始显着减慢。

背景

一些背景知识。该系统是一个完全同构的堆栈。最高级别App组件渲染模板、页面、dom 元素和更多组件。查看 React 代码,我发现它渲染了约 1500 个组件(这包括任何被视为简单组件的简单 dom 标记, <p>this is a react component</p>

在开发过程中,渲染约 1500 个组件需要约 200-300 毫秒。通过删除一些组件,我能够在大约 175-225 毫秒内渲染大约 1200 个组件。

在生产中,大约 1500 个组件上的 renderToString 大约需要大约 50-200 毫秒。

时间看起来确实是线性的。没有一个组件是慢的,而是许多组件的总和。

问题

这会给服务器带来一些问题。冗长的方法会导致服务器响应时间较长。 TTFB 比应有的要高很多。对于 api 调用和业务逻辑,响应时间应该是 250 毫秒,但是对于 250 毫秒的 renderToString,响应时间增加了一倍!对 SEO 和用户不利。此外,作为同步方法,renderToString()可以阻塞节点服务器并备份后续请求(这可以通过使用 2 个独立的节点服务器来解决:1 个作为 Web 服务器,1 个作为单独渲染 React 的服务)。

尝试

理想情况下,在生产环境中 renderToString 需要 5-50 毫秒。我一直在研究一些想法,但我不确定最好的方法是什么。

想法 1:缓存组件

任何标记为“静态”的组件都可以被缓存。通过使用渲染的标记保留缓存,renderToString()可以在渲染之前检查缓存。如果它找到一个组件,它会自动抓取该字符串。在高级组件上执行此操作将保存所有嵌套子组件的安装。您必须将缓存的组件标记的react rootID 替换为当前的rootID。

想法 2:将组件标记为简单/哑

通过将组件定义为“简单”,React 应该能够在渲染时跳过所有生命周期方法。 React 已经为核心 React dom 组件( <p/><h1/> 等)做到了这一点。扩展自定义组件以使用相同的优化会很好。

想法 3:跳过服务器端渲染上的组件

服务器不需要返回的组件(没有 SEO 值(value))可以简单地在服务器上跳过。客户端加载后,设置 clientLoaded标记为true并将其传递下来以强制重新渲染。

关闭和其他尝试

到目前为止,我实现的唯一解决方案是减少服务器上呈现的组件数量。

我们正在考虑的一些项目包括:

有人遇到过类似的问题吗?你能做什么? 谢谢。

最佳答案

使用react-router1.0和react0.14,我们多次错误地序列化了我们的flux对象。

RoutingContext 将为你的react-router路由中的每个模板调用createElement。这允许您注入(inject)任何您想要的 Prop 。我们也使用助焊剂。我们发送一个大对象的序列化版本。在我们的例子中,我们在 createElement 中执行 Flux.serialize() 操作。序列化方法可能需要大约 20 毫秒。如果有 4 个模板,您的 renderToString() 方法将额外花费 80 毫秒!

旧代码:

function createElement(Component, props) {
    props = _.extend(props, {
        flux: flux,
        path: path,
        serializedFlux: flux.serialize();
    });
    return <Component {...props} />;
}
var start = Date.now();
markup = renderToString(<RoutingContext {...renderProps} createElement={createElement} />);
console.log(Date.now() - start);

很容易对此进行优化:

var serializedFlux = flux.serialize(); // serialize one time only!

function createElement(Component, props) {
    props = _.extend(props, {
        flux: flux,
        path: path,
        serializedFlux: serializedFlux
    });
    return <Component {...props} />;
}
var start = Date.now();
markup = renderToString(<RoutingContext {...renderProps} createElement={createElement} />);
console.log(Date.now() - start);

就我而言,这有助于将 renderToString() 时间从约 120 毫秒减少到约 30 毫秒。 (您仍然需要将 1x serialize() 的大约 20ms 添加到总数中,这发生在 renderToString() 之前)这是一个很好的快速改进。 -- 重要的是要记住始终正确地做事,即使您不知道立即的影响!

关于performance - React renderToString() 性能和缓存 React 组件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34728962/

相关文章:

performance - 使用 CLRS 代码和 Robert Sedgewick 代码进行插入排序的运行时间差异

javascript - React.js 和 webpack - 为什么它不允许 var、let、const?

javascript - 同构 React 与 React Router 和 KOA

javascript - 多 React 组件架构

java - 如何在 webapp 中调试 OutOfMemoryException

python - 如何最有效地使用 collections.deque(popleft 与 appendleft)

c - 如何查看内存带宽是否已经成为瓶颈?

Javascript systemJS 错误找不到模块

javascript - 如何处理ReactJS中从组件B中的方法调用组件A中的方法的情况

node.js - 如何在 Node 后端使用浏览器库