Skip to content

Conversation

@nolanlawson
Copy link
Contributor

@nolanlawson nolanlawson commented Jun 6, 2022

Details

This is a stop-gap solution to #2851.

When we render components with shared stylesheets, the browser can optimize those stylesheets under the hood if the <style>s are byte-for-byte identical. (I don't have a benchmark handy, but just trust me that browsers do this. 🙂)

/* component.css */
@import './shared.css';

h1 { color: red }

Right now we concatenate a component's styles into a single <style>, including any shared stylesheets. So the browser cannot optimize, because the duplicated styles are embedded inside of separate <style>s that are not byte-for-byte identical.

This PR optimizes the engine so that it emits separate <style>s instead. The ordering is maintained, so that effectively you end up with the same styles being applied.

Does this pull request introduce a breaking change?

  • ✅ No, it does not introduce a breaking change.

Does this pull request introduce an observable change?

  • ⚠️ Yes, it does include an observable change.

A component may have more than one <style> node now. If component authors are trying to detect this (e.g. by using this.template.firstChild to get the <style>, or doing [...this.template.children].slice(1) to get everything but the first <style> element), then this PR will break them.

In practice though, developers already have to worry about the case of browsers that support constructable stylesheets versus those that don't. And on-platform, native shadow has not reached widespread adoption yet. So ideally this will not break anyone.

If developers really need to separate <style> from non-<style> tags, their best bet is:

this.template.querySelectorAll('style') // style tags
this.template.querySelectorAll(':not(style)') // non-style tags

Gus WI

W-11256353

@nolanlawson nolanlawson requested review from divmain and ekashida June 6, 2022 18:49
@nolanlawson nolanlawson force-pushed the nolan/separate-styles branch from 72e8c25 to 7e1ee1f Compare June 6, 2022 18:52
@nolanlawson
Copy link
Contributor Author

/nucleus ignore --reason "downstream failure is due to a flapping test"

@nolanlawson nolanlawson merged commit cc0cd60 into master Jun 7, 2022
@nolanlawson nolanlawson deleted the nolan/separate-styles branch June 7, 2022 22:30
@nolanlawson
Copy link
Contributor Author

Well, "trust me, it's faster" didn't sit well with me, so I wrote a benchmark. This benchmark renders 1,000 components with either 1) bootstrap.css as its own <style> plus an extra random <style>, or 2) the same stylesheets, but in one <style>. It measures the nav duration.

Surprisingly it looks like only Chrome actually has an optimization for this. In Firefox and Safari the two are basically the same, maybe even slightly slower in Firefox for the "separate styles" case.

Browser Concatenated styles Separate styles
Chrome 103 1207.19 365.5
Firefox 102 739 748
Safari 15.5 1000 929

In the Chrome profile, you can see that the time is saved mostly in "HTML parsing".

Screen Shot 2022-06-30 at 11 45 35 AM

It might also be worth testing this with shadow DOM too.

@nolanlawson
Copy link
Contributor Author

My benchmark doesn't consider shadow DOM; it also looks like the browsers have special logic for duplicate <style>s inside of shadow trees in particular: whatwg/dom#831 (comment)

So yeah, this change looks like a good one from a perf POV.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants