-
Notifications
You must be signed in to change notification settings - Fork 47.7k
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
Inline DOM property configs #10805
Inline DOM property configs #10805
Conversation
e506425
to
c64b73f
Compare
18be54f
to
1508ef3
Compare
791e3c1
to
82dcc93
Compare
Okay, the initial version is ready. Still need to test it with attribute table, add Flow types, and maybe simplify the callers. But curious to hear what you all think about this direction. |
I wonder how this will impact runtime performance as object lookups tend to be quite a bit faster than large switch statements – although things might be different as I've not looked into this for a while. Considering this is a hot path though, we should look into it more. I can help with this if needed :) I know other virtual DOM libraries recently started using ES2015 |
The |
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.
I like the approach of inlining over injection. Currently, there's an error with how this changes the behavior for unknown attributes, which I left a comment about.
Here's a small test case showing the issue that this introduces:
return setDOMValueAttribute; | ||
default: | ||
return null; | ||
} |
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.
Should we use a switch case here if there's only two possible return values? We could shave a off few more bytes with a ternary or if
/else
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.
I don't really care about this yet, as we'll likely change what's inside of these methods anyway. I'm mostly looking for feedback on the overall changes in how functions call each other.
// Removed when strictly equal to false; present without a value when | ||
// strictly equal to true; present with a value otherwise. | ||
case 'download': | ||
return 'overloadedBoolean'; |
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.
I wonder if it would minify better if we used constants instead of string literals?
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.
Yes, flowifying and turning into something simper is in the plan. (Although it's already smaller than the original.)
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.
AFAIK v8 used to/does deop switches without string literals as the case, I couldn't find anything recent that gave clarity on if that was still true, it may not matter here either. (I mention it because the return here is used in a switch elsewhere yeah?)
* Checks whether a property name is a writeable attribute. | ||
* @method | ||
*/ | ||
shouldSetAttribute: function(name, value) { | ||
if (DOMProperty.isReservedProp(name)) { | ||
if (name !== 'style' && DOMProperty.isReservedProp(name)) { |
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.
Why is style
being special-cased here now?
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.
It's the only reserved attribute that actually needs to appear in the DOM. Previously this was special cased by leaving it in the DOM config (even though the flag was set to 0
with style: 0
). Now we don't have a config so it has to go somewhere else.
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.
But in what case will shouldSetAttribute
be called for the style
prop? From what I can tell ReactDOMFiberComponent
always checks for the style prop early and defers to CSSPropertyOperations
. I don't think we're explicitly setting the style attribute anywhere.
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.
Is it the SSR code path? I didn't check that 😀
edit: I checked and, as expected, it isn't called for SSR either @gaearon
DOMPropertyOperations.setValueForAttribute( | ||
node, | ||
name, | ||
DOMProperty.shouldSetAttribute(name, value) ? value : null, |
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.
Doesn't this change the semantics of how unknown attributes are handled since the propertInfo
check is removed? Now unknown attributes aren't being handled by setValueForAttribute
so they aren't validated by isAttributeNameSafe
, which means that invalid attributes will now cause a fatal error, e.g
Failed to execute 'setAttribute' on 'Element': 'foo$$bar' is not a valid attribute name.
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.
In general I found the distinction between *ForProperty
and *ForAttribute
very confusing because it's often lying ("properties" can use attributes).
This indeed seems like a bug, can you write a test case for it?
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.
@gaearon I agree it's confusing, I wouldn't think that setValueForProperty
would be setting attributes. I pushed a commit to this branch with a failing test.
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.
I'd really like to get rid of this distinction if we can. It always felt a like to much indirection. At the very least, it would be nice if the property and attribute paths either had clearer separation, or we consolidated them and made sure the isAttributeNameSafe
validation is applied.
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.
I think as a rule of thumb, if you have an API like setValueForProperty
it shouldn't ever hit a code path that sets it as an attribute, and vice-versa. It's confusing that there's code like node[name] = value
inside shouldSetAttribute
checks. From what I can tell, the naming scheme is confusing two different types of properties: what we just call "props" and properties that we need to be set as a property (what shouldUseProperty
checks).
I also noticed that unknown attributes on SVG started respecting capitalization. e.g. I’m not sure if it’s bad or which change caused it. If you have ideas please share :-) |
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.
Overall, I really like this approach, but I am genuinely curious about the performance differences.
Still, this feels like a big maintainability win.
domElement, | ||
propKey, | ||
nextProp, | ||
); |
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.
If anything, I already appreciate the reduction in branching.
DOMPropertyOperations.setValueForAttribute( | ||
node, | ||
name, | ||
DOMProperty.shouldSetAttribute(name, value) ? value : null, |
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.
I'd really like to get rid of this distinction if we can. It always felt a like to much indirection. At the very least, it would be nice if the property and attribute paths either had clearer separation, or we consolidated them and made sure the isAttributeNameSafe
validation is applied.
} | ||
var mutationMethod = DOMProperty.getMutationMethod(name); | ||
if (mutationMethod) { | ||
mutationMethod(node, undefined); |
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.
Semi-unrelated, but method that this gets called for is value
, which has this null guard:
if (value == null) {
return node.removeAttribute('value');
}
I'm not sure if there is a big distinction between null and undefined in other parts of the code, but passing null
here, like mutationMethod(node, null)
feels better to me. What do you think?
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.
Really, I think if we stop syncing the value attribute with the value property, we can just drop mutation methods all together.
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.
We could just not pass the second argument at all since it will default to undefined
anyways, but I would prefer null
here as well. It's also shorter 😄
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.
Really, I think if we stop syncing the value attribute with the value property, we can just drop mutation methods all together.
I wonder if we could just inline the getMutationMethod
calls as well, since there's only one property that uses mutation methods. Like:
if (name === "value") {
setDOMValueAttribute(node, null)
}
Personally I think this is an enhancement, on the current 16 RC, if you do |
I haven't checked. Want to send a PR to get this check into attribute table? |
Regarding
Not sure why I didn't. Or maybe I missed it. |
@gaearon I tested |
Meh. I removed some of this indirection in other PRs and the rest isn't really helpful. |
This replaces DOM injections with static
switch
cases.I think this is a lot simpler to follow.
See individual commits. I took the strategy of getting rid of individual properties on the injected configs, then getting rid of
getPropertyInfo()
branching calls, and finally removing the configs.