-
Notifications
You must be signed in to change notification settings - Fork 47k
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
Optimizing Compiler: Inline ReactElements #3228
Comments
cc @sebmck |
What do you mean by using different transpilers? You'd just be using the same transpiler flick a switch unless I'm misunderstanding something?
Developers already know to use a different build of React for development and production so I don't think it'll be confusing to introduce the same concept to the transpilation step. I've been toying with the idea of having a production and development mode in Babel as there are things like TDZ that I really want to enable by default but would absolutely kill performance in hot code and their only purpose is to basically prevent development errors. |
Yea, that's what I meant. Developers already have minifiers that they only use in production mode. This is effectively the same thing. |
Ah, right. Yeah, definently more a developer education issue than a technical one. |
Nit: developer education is a technical issue. :) Look at how much technical trouble we go through to add warnings and locking down the API so that developers don't unknowingly shoot themselves in the foot. |
Yeah, absolutely agree. Probably should have been more specific, I was referring specifically to educating developers about any potential "production" mode in transpilers since there's no real way to warn them if they've compiled their production code in development mode. |
There were mentions in the past of "inverting" refs, you instead provide a ref-object to But... what if we implemented ref as a callback(s) instead which receives the instance as an argument and something to distinguish whether it was mounted or unmounted, kind of similar to how I imagine this could have other benefits in the future; expose all life-cycle callbacks and you could use it to say easily measure frontend objects without ever having to store the instance, nor provide complex logic to determine whether or not the frontend object actually updated. This exact use-case is just an idea, but this approach seems a lot more React:y, reusable and naturally efficient than Perhaps I'm way off on the utility here, but it seems to me that it solves a lot of problems that refs as meant to solve but in better ways and actually storing refs should only necessary when dealing with uncontrolled components. All other uses should reasonable be capable of doing its thing directly on instance when the callback is called. Re-implementing refs as-is could look something like the code below, it could easily be made into a reusable helper. It could take any number of shapes, perhaps a key isn't even interesting and just putting all instances in a Set is enough. You decide. function createRef(that, name, next) {
return {
next: next,
componentDidMount: function(instance) {
that.refs[name] = instance;
},
componentWillUnmount: function(instance) {
delete that.refs[name];
}
};
} To keep updated measurements of a component you would simply as below, if the editor didn't update then you automatically avoid re-measuring for free. If only the editor updates, but not your own component, you still remeasure and get the updated height.
|
@syranide We already implemented both first-class refs and then switched them to callbacks in #2917. You haven't been keeping up. :) It is not very convenient and very imperative since you need to keep your own reference to it. The timing of life-cycles isn't ideal too. Therefore I'm not comfortable deprecating the current refs until we're sure which model is final, and make sure that we have a good upgrade path (codemodable). |
@sebmarkbage * facepalm * ;) |
I've done a complete and stable implementation of this (see attached commit). Also works perfectly with #3226 (no real reason for it not to). |
@sebmarkbage |
This proposal now need to be revised to include a |
@sebmarkbage shh I was working on a PR! now @sebmck will beat me for sure… |
Performance question: is it preferable to always include the |
@elsassph keeping those properties allows JIT compilers (such as V8) to match the "hidden class" of the objects. Generally speaking this should give better performance. |
@trueadm You'll benefit from that anyway, there will just be more "hidden classes". I suspect there's nothing to be gained in this context, the performance difference is small and spread over the whole project it's unlikely to even be measurable and if you overdo it you're actually increasing the cost as a result of dealing with larger objects and more processing involved in creating them (again probably immeasurably). So I would say it's largely counter-productive, if you're rendering large lists it may be a good idea to keep the signature the same (so the hidden class is kept in fast cache), but even then I doubt it's really measurable. |
@syranide The performance can definitely make an impact. As you point out, it's very good for large lists of virtual nodes, where you need to aggressively diff them. This is one of the many reasons why Inferno gets very good performance, especially when it comes to complex node tress that rapidly update. Keeping object shapes monomorphic can give significant benefits in regards to V8 and how it decides what to |
Generally there is a single code path being repeated and it's a very unusual setup if you're mixing keys with no keys and refs with no refs in a list.
Yes, but preemptively adding |
@syranide It looks great on benchmarks if done right, but in the grand scheme of things, it's unlikely to make much difference as the app code the user has created is most likely to be the bottleneck in any case, but it's worth having if you can get it for free at a compiler level. |
Thanks that's the kind of considerations I'm facing - making objects smaller by not including null properties VS shadow classes potential benefits. |
On the React team we've been operating under the assumption that it's better to include the nulls. I'm going to close this since the babel plugin now exists. |
Starting with React 0.14 we will be able to inline ReactElements:
as objects:
This improves performance over the existing
React.createElement
call by inlining the result of it.defaultProps
If a component might have default props they need to be resolved by the transpiler's runtime:
Exception: ref="string"
Unfortunately we still haven't figured out what the final semantics for refs. The current semantics relies on getting the current React owner. Therefore, we cannot apply this optimization if the ref attribute might be a string.
Non-JSX
This can work on React.createElement or functions created by React.createFactory if the first argument is an inline object literal. Otherwise it is not safe since the object might be reused and mutated.
Only in Production Mode
This optimization should only be applied in production mode. Currently
React.createElement
fires various warnings for propTypes and key warnings if the__DEV__
flag is set to true or"production" !== process.env.NODE_ENV
. This optimization would skip the warnings which would be very very bad in development mode.The difficult part of this is figuring out a way that this will work in everyone's environment because not everyone has the ability to use different transpilers for development and production mode.
One solution might be to use a ternary and rely on minifiers to strip out the extra code:
This will a pain for source maps though.
Another solution would be to have different flags in the transpilers themselves but we'd have to make sure that people actually use them correctly. They will otherwise have problems due to not firing warnings, or think that React is slow because they screwed up their config.
The text was updated successfully, but these errors were encountered: