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
Handle shared attributes object in hyperscript #1942
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See my comment for details.
But do note: we are limited to ES5 (excluding Promises) to support IE9/10 and many Android browsers, so yes, Object.assign
is out of the window.
Also, if you could, please add a line to the changelog explaining this change (it's a bug fix, but it will affect performance optimization). It's not critical, but it'd make our lives easier.
render/hyperscript.js
Outdated
attrs = Object.keys(attrs).reduce(function(newAttributes, key) { | ||
newAttributes[key] = attrs[key] | ||
return newAttributes | ||
}, {}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just use a (hasOwnProperty
guarded) for ... in
loop. Keep in mind this is in a somewhat performance-critical place, and we'd rather not be creating a closure here.
Also, this copying should only be done in execSelector
, preferably only if state.attrs
there is non-empty. That way, it is only done when it's absolutely necessary, not on every call.
Keep in mind, each render could literally create thousands of these nodes each time. It has to be fast in the common path.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@isiahmeadows thanks for clarification! I will look into that in a day or two.
c041eac
to
9e6b175
Compare
@isiahmeadows I have updated PR:
Please note that I had to amend existing commit to include issue id there. |
render/hyperscript.js
Outdated
@@ -28,6 +28,18 @@ function execSelector(state, attrs, children) { | |||
var hasAttrs = false, childList, text | |||
var className = attrs.className || attrs.class | |||
|
|||
if (Object.keys(state.attrs).length && Object.keys(attrs).length) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note to self for later (if nobody beats me to it): introduce a more optimized helper in this file to check if an object has any properties.
function isEmpty(object) {
for (var key of object) if (hasOwn.call(object, key)) return true
return false
}
(Nothing mandatory on your part @s-ilya)
Would you |
@tivac I'll point out that part of the perf issue you'll catch is the So if you implement that before benchmarking, you'll probably glean a better understanding of the end performance impact. |
That's fine, I just want to understand the impact given that even when this is rewritten it looks like it'll require creating a brand-new |
@tivac Also, I did have @s-ilya move the copying logic to where it would have far less of a performance hit in practice. (Note: we're already copying the attrs object from parsed selectors to begin with. The difference is that it's not allocated then, but small object allocation is surprisingly cheap.) |
@tivac @isiahmeadows please see results below: without changes, with changes and with changes + isEmpty function. Without changesmedle@DESKTOP-A0U7GBR MINGW64 ~/mithril.js (next)
rerender without changes x 2,254,951 ops/sec ±2.74% (89 runs sampled)
rerender without changes x 2,222,602 ops/sec ±2.16% (87 runs sampled)
rerender without changes x 2,170,630 ops/sec ±2.96% (84 runs sampled)
rerender without changes x 1,887,043 ops/sec ±1.97% (83 runs sampled)
rerender without changes x 1,845,580 ops/sec ±4.47% (78 runs sampled) With changes from this PRmedle@DESKTOP-A0U7GBR MINGW64 ~/mithril.js (hypertext-shared-attrs)
rerender without changes x 2,183,097 ops/sec ±2.31% (89 runs sampled)
rerender without changes x 2,307,716 ops/sec ±2.03% (91 runs sampled)
rerender without changes x 2,317,578 ops/sec ±1.71% (87 runs sampled)
rerender without changes x 2,179,205 ops/sec ±3.39% (87 runs sampled)
rerender without changes x 2,242,537 ops/sec ±3.22% (87 runs sampled) With changes from this PR + isEmpty functionmedle@DESKTOP-A0U7GBR MINGW64 ~/mithril.js (hypertext-shared-attrs)
rerender without changes x 2,352,263 ops/sec ±1.82% (90 runs sampled)
rerender without changes x 2,355,014 ops/sec ±1.85% (90 runs sampled)
rerender without changes x 2,272,959 ops/sec ±2.98% (92 runs sampled)
rerender without changes x 2,282,442 ops/sec ±2.19% (90 runs sampled)
rerender without changes x 2,191,162 ops/sec ±3.73% (86 runs sampled) |
@s-ilya awesome, thanks for the info. My perf concerns are quelled! |
@s-ilya Since you already tried it with the |
@isiahmeadows sure! |
Full merge ahead! 😄 |
Possible fix for Shared attributes for different component results in concatenated class names #1941
Mithril modifies passed attributes object to store classname derived from selector. I suggest we make a copy so there will no unexpected side-effects.
btw, I'm using custom shallow copy instead of
Object.assign
because mithril seems to support IE9 and 10. Is it still the case?