-
Notifications
You must be signed in to change notification settings - Fork 46.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Server rendering is slower with npm react #812
Comments
|
Lately I have been simply using a wrapped module var React;
if (process.env.NODE_ENV !== 'production') {
React = require('./react-with-addons-0.10.0.js');
} else {
React = require('./react-with-addons-0.10.0.min.js');
}
module.exports = React; Works great. You could consider switching npm package to some such scheme if there are no downsides. |
My first attempt was to just add function x() { return __DEV__; }
module.exports = x(); transforms into if (process.env.NODE_ENV !== 'production') {
var x = function x() { return true; };
module.exports = x();
} else {
var x = function x() { return false; };
module.exports = x();
} so we only pay the getter cost once (per module) at require time. @benjamn Is doing this easy with recast? It's not obvious to me if |
I would love to see this get some attention. 30% improvement out of the box would be awesome. Is there a lot of work to do here? |
I think eventually we will want people to do a build step for server rendering, i.e. webpack's |
I'm using webpack and |
For browserify there's
Thus browserify also can be used for bundling for server rendering. |
Fixes facebook#812. Previously, this code module.exports = moo(); function moo() { return __DEV__; } would be transformed to module.exports = moo(); function moo() { return "production" !== process.env.NODE_ENV; } Now, it's transformed to: if ("production" !== process.env.NODE_ENV) { var moo = function() { return true; }; module.exports = moo(); } else { var moo = function() { return false; }; module.exports = moo(); } which reduces the getter cost to one test at require time instead of inline for every `__DEV__` check, warning, and invariant. The unminified build is about twice as large now (but it's about the same after gzipping) and the minified build is the same size.
Call me all crazy, just had a left field idea. Could the transformer that builds the npm module create 2 complete copies of react? one for dev and one for production? In the the normal version of react |
That's not too crazy. It's a bit annoying from a build process though. I'd love to know what's happening at a larger scale in the node community. It seems like we shouldn't be the first people to encounter this in a larger scale. A couple other ideas… What if we set Another idea: Have a If all you're using is React and nothing from |
I don't think we're going to do anything specifically for server rendering other than bundling the builds in |
just curious why if (process.env.NODE_ENV !== 'production') {
var x = function x() { return true; };
module.exports = x();
} else {
var x = function x() { return false; };
module.exports = x();
} and not module.exports = (process.env.NODE_ENV !== 'production'); ? |
It was just an example. If that was the actual code I'd do the direct assignment like you suggested. |
ah, ok - thanks :) |
any movement on this? |
No movement, though you can already require the builds from react/dist as @zpao mentioned above. |
@syranide since you mentioned Webpack, do you use Webpack for server side code? |
If you're already using babel on your server-side code (say, via |
As I mention in that ticket, this is the main problem: // This would work if it were a const
var NODE_ENV = process.env.NODE_ENV;
//...
if(NODE_ENV !== 'production') { // this will not be eliminated
// debug code
} A |
That seems like a decent solution to me. I'm personally not a fan of the use of a global |
@mhart I would be happy to do that, but it is important that we have a way to eliminate dev-only code in prod builds and that users of React from npm have a way to do the same. Obviously we can use whatever tools we want for our stack easily but for npm currently we suggest envify/webpack.DefinePlugin. Not inherently opposed to changing that but we'd need a good proposal and reason. |
@spicyj yeah, I hear ya – there's a lot of code now in React that depends on that behaviour. Not sure I fully understand your comment "for npm currently we suggest envify/webpack.DefinePlugin" – what do you mean "for npm"? React code is compiled before it's published to npm, right? My general feeling is that it would be great if there were no FB/React-specific idioms in the code – so that it's treated as though, if v8 (or other engines) had all the ES2015/2016 features needed, then you could run the code on Node.js without even needing a compiler. Currently that's not really the case. (I'm sure you could do some sort of If there were a way such that |
I mean: if you use webpack or browserify in conjunction with react from npm, you should be able to eliminate React dev-only code from your prod builds. envify lets us do that easily in browserify as it copies the NODE_ENV from when you make your build, and webpack.DefinePlugin lets you configure your build to replace
Yes, if it works for the case of React devs using browserify/webpack too, not just our premade builds. |
@spicyj as an aside, just looking through the code now, and aside from the global |
Well, it does set up the team to move to ES6 for I've submitted a PR to UglifyJS2 as you can see above. If it's accepted this will solve the problem. |
@STRML We'd still need everyone using React to upgrade to that version of UglifyJS so I can't promise that we'll take it… |
Just everyone using React in combination with webpack or browserify and not using the Alternatively they could use the DefinePlugin to set |
@spicyj just to clarify, do you mean "everyone using React who also currently uses UglifyJS with it"? |
Just so I'm clear, there are a few users here that I can think of. Those, on the browser:
So 1 and 4 are taken care of by the compilation in |
2 and 3 are the most natural for many people. If you are using browserify then envify gets used automatically because of our config in package.json. Almost everyone minifies their code in prod. It is true that we could recommend #4 instead for many cases, but it does fall apart in the case of requiring submodules. We don't support this for external users because we consider the modules private but the addons packages use this pattern. Various third-party projects (unsupported by us) also make use of this. |
I think the only reason 2 and 3 are "most natural" is just because I use 4 whenever I can and it's no more complicated than 2 or 3 for normal usage – I guess it's just a pity that users are encouraged to "reach in" to Thanks for the clarification. So it's those doing 3 who are expecting their current UglifyJS setup to eliminate any code using |
To clarify: we recommend users require
Yes. |
Ah cool, good to know. |
this gets rid of some runtime checks against the process.env property, which slows down node A LOT, see facebook/react#812
We have been bitten by this one too. Would be great if some note about this behavior was present on https://facebook.github.io/react/docs/environments.html , it's a large performance impact, and is hard to find this issue unless you have already done all the investigation work. |
I would like to add that this issue also affects Electron applications. Now the work arounds seem to be:
Note that replace every Why is this issue closed? |
Technically this could be fixed now as UglifyJS supports In combination with Rollup, it would be simple to export a |
Adding my two cents here, I was able to work around this by adding the following to the top of my
I had previously wrapped React using local modules |
For future reference, the above snippet is broken per: #8788 |
Referencing facebook/fbjs#86 which will fix this now that UglifyJS can properly eliminate dev code (even if it references a See facebook/fbjs#86 (comment) for more context. |
Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react-forget/pull/812). * __->__ #812 [test] Print HIR before leaving SSA Looking at the HIR before it leaves SSA helps debugging better.
I ran a few benchmarks on the server (modifying https://github.com/paulshen/react-bench to not use jsdom). The results were surprising, as the browserified
react.js
was about 30% faster than the npm version, even withNODE_ENV=production
.The performance ranking (test run time) was
react.min.js
<react.js
<NODE_ENV=production node react
<node react
.I suspect
process.env
is not a regular js object but perhaps a getter and thus carries a penalty when you test for"production" !== process.env.NODE_ENV
everywhere.Also the minified version might still perform best of all, as at least some time ago V8 used function source length (including comments) as a heuristic for function complexity / compilation time and thus affecting chances for optimization in some cases, but the effect might be negligible.
The text was updated successfully, but these errors were encountered: