11 ms·
The Performance Cost of Server Side Rendered React on Node.js
- kraftman 9y agoI thought the idea was to render it server side just once, then have the client take over for all future requests? In which case it's 10x slow for the first page load, but all future requests are just hitting API endpoints and can be fairly fast?
- deleted 9y ago[deleted]
- nathan_long 9y agoSure, but every user has to do that initial page load. If all initial page loads are slow and expensive, it affects the user experience and costs money. Also, if you start getting requests faster than you can do the initial render, each subsequent user has to wait in line longer than the one before. Your server-side framework won't even show you how long people waited for a TCP connection.
- kraftman 9y agoBut they're going to have to wait for the initial page load either way, whether it's rendered server side and sent over or they're shown some static content until its rendered client side?
- pmontra 9y agoThey do, but the load will be spread among clients. The issue here is the load on the server.
- kraftman 9y agoI guess it depends on which direction you're coming from. From a traditional server side point of view, you're reducing the server work to just that first page load and making the client do the rest.
- nathan_long 9y agoTrue. But making all server-side renders really efficient and fast is an option, too. http://nathanmlong.com/2016/11/elixir-and-io-lists-part-2-io-lists-in-phoenix/ http://nathanmlong.com/2016/11/elixir-and-io-lists-part-2-io...
- deleted 9y ago[deleted]
- scyber 9y agoIf the Server rendered page is not user specific and is generic for all users then it can be cached and served quickly. Then only potentially one user would experience a slowdown and you can serve much faster. Of course then you get into the fun of cache management and invalidation.
- nathan_long 9y agoElixir Phoenix has a game-changing approach here because of how it compiles templates. Effectively, the static part is always cached (until the template changes), but the dynamic part is never cached. Nothing to manage. See http://nathanmlong.com/2016/11/elixir-and-io-lists-part-2-io-lists-in-phoenix/ http://nathanmlong.com/2016/11/elixir-and-io-lists-part-2-io... for explanation.
- _greim_ 9y agoCorrect, except that initial app load time for un-cookied users with cold browser caches is a critical metric, since it largely determines whether they'll keep using your app. Fortunately, those are the scenarios where server-side caching can have a decent benefit, since you're essentially rendering the same view to all such users.
- Swizec 9y agoIf all first time users see the same thing, why not serve a precompiled static html that then gets taken over by React. Isn’t that the whole point?
- wickedlogic 9y agoSo very much this. Particularly if you dont have enough context to be personalized, pre-compiled popular segments are a reasonable approach.
- abritinthebay 9y ago> If all first time users see the same thing That's a big, important, if.
- _greim_ 9y agoIt's good odds, though. Obviously depending on the app, these would be non-logged-in users who don't get personalized content.
- abritinthebay 9y agoThere can be (though you’d be surprised- there’s rarely such a thing as a non-identifiable user nowadays). Just pointing out it’s a very big conditional. For example: I work at Bleacher Report and our non-logged-in users get data customized for them. That can be as simple as GeoIP data or can be us knowing (and inferring) what articles or content they’ve seen. The permutations are quite large. Now some blog, or a very simple app won’t do something like that (hell, they might not have the depth of content to be able to) but it’s reasonbly common to have that requirement on large sites outside of v.limited pages (like login/signup etc)
- merb 9y agowell wasn't that clear? Isn't something like a Template Engine quite CPU intensive? Things were node.js performce poorly?
- ralusek 9y agoThey include a comparison with using ES6 Template literals (native JavaScript string interpolation), and saw 10x increase over React. The native string interpolation actually came out just behind serving straight static files. JavaScript CPU performance is actually quite high among interpreted languages. So to answer your question more specifically, no I don't think it is clear. What this is specifically isolating as the performance problem is that using React as a template renderer on the backend is not performant, not because of NodeJS performance, but because React is realistically not really specialized for this.
- foota 9y agoDoes anyone actually use vanilla JavaScript string literals for all their backend rendering?
- ralusek 9y agoCouldn't tell you, but it's not really relevant. The post I'm replying to says that it should come as no surprise that NodeJS template rendering is slow, and I just meant to point out that the article is quite specifically isolating ReactJS rendering as slow, and has nothing to do with NodeJS or JS at all.
- merb 9y agowell depends, I doubt that native js string interpolation is written in javascript at all. so basically it is javascript, but since you are using the native interpolation you probably go trough a c interface. or some kind of extremly optimized path.
- jerven 9y agoAll no, but quite a bit, in our prototype for our next gen codebase. Was quite nice because we had a lot of 'legacy' JSPx code that we transformed into JS code using a bit of one off programming with some clean up afterwards.
- deleted 9y ago[deleted]
- makmanalp 9y agoSo I thought the entire point of SSR was to speed up initial page load - if you pre-render all the static parts of the page, including what happens after routing, but before any API calls, so that your webserver can cache that static HTML for that route and serve it up. What this means is that the user immediately sees much more of a rendered HTML page, rather than seeing a blank page for 2 seconds and having to wait to download and run a massive JS bundle first.
- qaq 9y agoOK from single core $10 VPS you will be able to serve 1,080,000 unique initial loads per hour and depending on your pattern of traffic say 15,000,000 in a day could you remind me what the issue is again?
- foepys 9y agoThe issue is that it could be ~14,040,000.
- dustingetz 9y agoIt's a real issue, my startup's react SSR times are ~5 seconds of blocking the node thread on my $40 droplet. It's about 50% react. Our pages are way more sophisticated than your average page with like a query and a form, though. There are many ways to make faster though, one of which is to simply configure your CDN properly.
- abritinthebay 9y ago5 seconds per request?? What on earth? Our entire app is React+Redux based, gathers a bunch of unique user data per-request from various apis and we still get ~40-120ms response times (and that's on CDN cache misses). Hell, our Node server's connection timeout is 3 seconds and we only ever hit that due an APIs tanking or something.
- qaq 9y agodid you ever consider that you might not be doing something right ?
- dustingetz 9y agoNot really, I'm the consultant people hire when they need to solve issues like this. There are some screenshots of what we're doing here: http://www.hyperfiddle-consulting.com/ http://www.hyperfiddle-consulting.com/ http://hyperfiddle.net/ http://hyperfiddle.net/ As I said, our app is sophisticated
- deleted 9y ago[deleted]
- k__ 9y agoRender on write, not on read.
- styfle 9y agoCould the performance be improved if you call React.createFactory() and then stream the factory call like this[0] code? [0]: https://github.com/styfle/react-server-example-tsx/blob/4586f3431b4bfd424179da3aad2c6adbddb0de10/src/server.tsx#L12 https://github.com/styfle/react-server-example-tsx/blob/4586...
- miohtama 9y agoDoes React build any cached or compiled intermediates for its templates?
- MatthewPhillips 9y agoNote that React doesn't do streaming HTML in the traditional sense. You can't send out initial HTML while you wait on an API response, for example. Instead only the serializer is streaming. Your app renders to a VNode synchronously and then the serializer traverses the VNode, synchronously, and streams the strings out as it goes. This is why streaming is only a little bit better in these numbers. If/when React gets real streaming it will compete better with traditional templating engines, most of which don't do streaming. If you use Node.js and want a traditional templating engine that uses JS template literals and does do streaming, check out my project: https://github.com/matthewp/flora https://github.com/matthewp/flora
- bringking 9y agoReact 16 supports streaming! https://reactjs.org/blog/2017/09/26/react-v16.0.html#better-server-side-rendering https://reactjs.org/blog/2017/09/26/react-v16.0.html#better-...
- bringking 9y agoOh sorry I should read better! Sorry
- styfle 9y agoI believe you can stream the <head> while still waiting for any react components to render like so: https://github.com/styfle/react-server-example-tsx/blob/4586f3431b4bfd424179da3aad2c6adbddb0de10/src/server.tsx#L22 https://github.com/styfle/react-server-example-tsx/blob/4586...
- MatthewPhillips 9y agoOf course, you can stream parts of your page that you don't use React. That should be obvious. :)
- dustingetz 9y agohttps://github.com/walmartlabs/react-ssr-optimization https://github.com/walmartlabs/react-ssr-optimization React definitely needs work here and i think the right people are quite aware of the issues.
- kennu 9y agoIsn't the whole point of server-side rendered React to render it just once, when building the website, and then serve it as static HTML/JS/CSS assets using a static webserver?
- OoooooooO 9y agoHow do you do that with dynamic content?
- kennu 9y agoIn my view the user-specific parts and frequently updating data should be rendered on the client side. The pre-rendered HTML should contain the less-frequently changing information that is important for SEO.
- always_good 9y agoGiven the option, and pretending it were just as easy to implement either, why would you wait until the document loads to fetch resources via Javascript when you can render the content on the server? The article points out one downside: React's render-to-string faculties are slow.
- abritinthebay 9y agoThat would be one way of using it. Though practically speaking a very limited way of doing it and not very useful for a lot of dynamic sites.
- andrewwharton 9y agoThat's one use case. The other is for performance/seo where the initial markup for the app is generated and served to the client as HTML, then the React app bootstraps on the client and takes over managing the DOM.
- richdougherty 9y agoThis is what I'd like to see a performance comparison for. 1. Initial load using client-side vs server-side rendered. (Server-side should be faster.) 2. Warm load using client-side vs server-side rendered. (Client-side should be faster.) Comparing to static is interesting, but not really an option if you're serving dynamic content.
- olegkikin 9y agoRelated: Node.js templating engine benchmarks: https://github.com/marko-js/templating-benchmarks https://github.com/marko-js/templating-benchmarks Framework SSR benchmark (Vue, React, Preact, Rax, Marko): https://hackernoon.com/server-side-rendering-shootout-with-marko-preact-rax-react-and-vue-25e1ae17800f https://hackernoon.com/server-side-rendering-shootout-with-m...
- arkh 9y agoI'd like to see the results with php through node-phpcgi to render those pages. So we'd have gone full circle at last.
- simplify 9y agoI'd love to see a side-by-side comparison to Marko.js (https://markojs.com/ https://markojs.com/). It has a lot of react-like features, but was built from the ground up for SSR speed.
- mattrick 9y agoMarko keeps some bechmarks here: https://github.com/marko-js/isomorphic-ui-benchmarks https://github.com/marko-js/isomorphic-ui-benchmarks Not too sure how accurate or biased they are, though.
- tabeth 9y agoI'd be curious to know if this cost is similar with Ember's implementation of SSR, Fastboot.
- pteredactyl 9y agoBeen using template literals for basic apps instead of the usual overhead. Mostly loving it.
- mottomotto 9y agoIf you're on the fence about server-side rendering being needed for your webapp don't do it! As a team of two with one engineer, I decided YAGNI and instead focused on the responsiveness of the React and Redux-based web application. It is very snappy to load and our (paying) customers are happy. You might not need SSR. I don't. I expect there will be more work to optimize it and eventually it'll be obviously a good idea. But you don't need it right now if you're working on a SaaS product.
- spinlock 9y agoAlso, if you _need_ SSR, rails might be a better approach. It's too bad the author didn't include it in the benchmark.
- lucaspiller 9y agoWe did this recently with a small app that needs to be fast. The relatively simple page that you see when you first visit the site is rendered with plain old Rails, then when the user interacts with the page (based on DOM addEventListener hooks) it causes additional React components to be mounted and rendered. Originally I’d hoped to be able to render a basic version of the page with Rails, then ‘enhance’ it with React - ie render <button>For</button> with Rails, then when React has loaded, replace it with a React component. But I couldn’t find any details on using it this way, and given time constraints choose the simplest option.
- styfle 9y ago> then when React has loaded, replace it with a React component What you described is basically `hydrate` which is how to enable React event handlers like onClick after a server-side render. https://github.com/styfle/react-server-example-tsx/blob/4586f3431b4bfd424179da3aad2c6adbddb0de10/src/browser.tsx#L11 https://github.com/styfle/react-server-example-tsx/blob/4586...
- scaryclam 9y agoThis is something we've done in the past, and it works well. In fact, the ability to do this (usability is important to us so progressive enhancement is a lifesaver) is why we looked at react in the first place.
- jondubois 9y agoThese results are not surprising to me but it's great that they were published because people have been very quick to jump on the hype bandwagon for these kinds of projects. I think that the idea of the isomorphic JavaScript app never actually worked in practice. MeteorJS already had a really good go at it. Sharing JavaScript modules between the client and the server is great but when you start to pretend that the backend and the frontend are the same environment, you're bound to run into all sorts of problems and create inefficiencies. The kind of performance penalty incurred by server-rendered React is bound to make your system vulnerable to DoS attacks or at best make it unnecessarily expensive to operate. When programming abstractions shift too far away from the reality of the underlying architecture, you will start paying dearly and the cost won't be worth it in the long run. In my opinion, the client-server divide is a fundamental aspect of multiuser applications and no amount of abstraction can make it go away.
- yoshuaw 9y agoIf you're looking for DOM in the front, Strings in the back, check out Bel – https://github.com/shama/bel https://github.com/shama/bel
- STRML 9y agoFor this reason, we don't use SSR for pages that change often, and turn it off completely for authenticated users. Some performance analysis showed it was actually slower in the average case than shipping the JS and having the client render it themselves, especially with a hot cache. This still provides what we wanted anyway; the ability for pages to easily be viewable without JS and easily indexed by search engines.
- laurencerowe 9y agoIt would be worth verifying that NODE_ENV=production is set during these benchmarks (I see no mention in either the article or the source repository.) Running in development mode has a significant performance impact. Edit: This PR shows that is likely to be the case. Running in production mode along with some other minor changes shows React running at 88% the speed of Pug. https://github.com/janit/node-templating-benchmark/pull/1 https://github.com/janit/node-templating-benchmark/pull/1
- anatoli 9y agoNODE_ENV=production definitely wasn't set based on some basic testing I did (that's my PR there), which accounts for the atrocious performance. Also, the React component is created on each request instead of just being created once. Unless one's goal is to specifically colour the perception of React, I don't understand that decision alongside the code for Pug which precompiles the template a single time. (Also declaring the map function outside the component and not creating a closure makes for a tiny extra boost.) It's disappointing to see that the article took off to this extent given how misleading and inaccurate it is.
- zghst 9y agoIt’s common in the dev (esp. JavaScript) community. The wild rush to be first to discover or disagree for karma points on the internet.
- velmu 9y agoHello Anatoli. Thank you for this. It was clearly an oversight on my part, not intentional to make React look bad. I have added a note on the article and will run the tests with the correct settings and your PR with further optimisation.
- jeswin 9y agoThen you should withdraw the article until you get real numbers. This utterly misleading data (as of now) is going to be picked up by search engines and other blogs. Add: on a positive note, this aspect of React is worth benchmarking. Thank you for that.
- ec109685 9y agoThe author mistakes increasing concurrent request as being some sort of proxy for concurrent users. Once you have maximized rps or maximized cpu, increasing concurrent just artificially hurts your average latency. Max RPS equals number of users you can serve per second. Concurrency should just be tweaked until you eliminate the network latency of your test framework.
- opvasger 9y agoMy clients get to render their own html - it's not really that bad if you bundle, split and cache the code.
- justin808 9y agoDid anybody try https://github.com/shakacode/react_on_rails https://github.com/shakacode/react_on_rails for this comparison?