Skip to content
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

Startup optimizations #226

Merged
merged 6 commits into from
Nov 9, 2018
Merged

Startup optimizations #226

merged 6 commits into from
Nov 9, 2018

Conversation

developit
Copy link
Collaborator

@developit developit commented Oct 20, 2018

  • Prerender ("static SSR")
  • Critical CSS inlining (ends up just being the intro UI, seems to work well)

I played with both to get the HTML payload to 2.27kB.

Also fixes #221. <script defer> in head actually performed worse than <script async> at the end of body with a preload in head, though I didn't test exhaustively (mostly successive LH runs).

@jakearchibald
Copy link
Collaborator

jakearchibald commented Oct 22, 2018

This is looking good. Couple of points:

I disagree with what we're doing with the font. The render is now unstable during load, and the instability affects all our areas of first interactivity. I think the best options are sticking with the base64, or removing the font entirely.

With the font:

screen shot 2018-10-22 at 10 14 48

Without the font:

screen shot 2018-10-22 at 10 14 26

@developit @kosamari @surma what do you think? Should we just drop the font?

We shouldn't render interactive elements that don't yet have behavior attached. We should either ship a very small bit of script that can handle the interaction, but I think it's enough to just hide those elements. I guess PRERENDER would let us do this?

I think our pre-JS render should be limited to:

screen shot 2018-10-22 at 10 24 32

@@ -47,7 +49,7 @@
}

.open-image-guide {
font: 300 11vw intro-text;
font: 300 11vw intro-text,helvetica,arial;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, sans-serif has the same effect as helvetica,arial.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iirc just helvetica would as well, since windows substitutes Arial anyway. good catch.

@surma
Copy link
Collaborator

surma commented Oct 22, 2018

I think we should inline a subset font for the first render, mainly because of two reasons:

  • Lots of web devs get told “Use our CI’s webfont or bust”. We have the chance to them that you can do that without paying a ridiculous cost.
  • We have the budget. Our TTFCP/TTI is amazing, we have available size budget to inline the font. Yes we could be smaller, but that’s what budgets are for.

@developit
Copy link
Collaborator Author

I'm fine inlining the font or removing it. TBH I'd rather spend those bytes on inlining handlers for drop+click that enqueue to get our TTI down.

@developit
Copy link
Collaborator Author

developit commented Oct 29, 2018

Alright here's the latest result with the fonts inlined and scripts back to defer in the head.
I think we're good?
screen shot 2018-10-29 at 10 48 38 am

@@ -10,6 +11,7 @@
font-family: 'intro-text';
font-style: normal;
font-weight: 500;
font-display: fallback;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this do anything now we're inlining?

Copy link
Collaborator Author

@developit developit Oct 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The data URLs do take a few ms according to lighthouse, but I guess that blocks rendering anyway. Would it make sense to switch this to block, or just remove it?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah make it block. That covers browsers that might allow a swap (Edge, maybe?)

src/index.ts Outdated Show resolved Hide resolved
src/index.ts Outdated Show resolved Hide resolved
Copy link
Collaborator

@jakearchibald jakearchibald left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few nits in the comments, but the main problem is this isn't building. I'm seeing the same problem Netlify is.

ERROR in   TypeError: Cannot read property 'readFile' of undefined

  - critters.js:97
    [squoosh]/[critters-webpack-plugin]/dist/critters.js:97:19

  - new Promise

  - critters.js:96 Critters.readFile
    [squoosh]/[critters-webpack-plugin]/dist/critters.js:96:12

  - critters.js:153 Critters.<anonymous>
    [squoosh]/[critters-webpack-plugin]/dist/critters.js:153:25

  - new Promise

  - critters.js:150 Critters.<anonymous>
    [squoosh]/[critters-webpack-plugin]/dist/critters.js:150:16

  - new Promise

  - critters.js:139 Critters.embedLinkedStylesheet
    [squoosh]/[critters-webpack-plugin]/dist/critters.js:139:12

  - critters.js:114
    [squoosh]/[critters-webpack-plugin]/dist/critters.js:114:83

  - Array.map

  - critters.js:114 Critters.<anonymous>
    [squoosh]/[critters-webpack-plugin]/dist/critters.js:114:47

  - new Promise

  - critters.js:106 Critters.process
    [squoosh]/[critters-webpack-plugin]/dist/critters.js:106:12

  - critters.js:65
    [squoosh]/[critters-webpack-plugin]/dist/critters.js:65:24


  - new Promise


  - Hook.js:154 AsyncSeriesWaterfallHook.lazyCompileHook
    [squoosh]/[tapable]/lib/Hook.js:154:20

  - index.js:673
    [squoosh]/[html-webpack-plugin]/index.js:673:47

  - index.js:196 Promise.resolve.then.then.then.then.then.then.then.then.result
    [squoosh]/[html-webpack-plugin]/index.js:196:18

  - next_tick.js:68 process._tickCallback
    internal/process/next_tick.js:68:7

@developit
Copy link
Collaborator Author

update: fixed the readfile stuff, but I'm still struggling to get both compilers producing the same contenthashes. Taking forever.

@developit
Copy link
Collaborator Author

Root cause turned out to be this bug that was fixed last month. Updating and tweaking a couple things fixed hashes and thus the build:
webpack-contrib/mini-css-extract-plugin#281

@developit
Copy link
Collaborator Author

developit commented Oct 31, 2018

final result on emulated 4g / 4x slowdown

screen shot 2018-10-31 at 12 40 07 am

@@ -110,7 +110,7 @@ export default class Intro extends Component<Props, State> {
<div class={style.demo}>
<div class={style.demoImgContainer}>
<div class={style.demoImgAspect}>
<img class={style.demoIcon} src={demo.iconUrl} alt=""/>
<img class={style.demoIcon} src={demo.iconUrl} alt="" decoding="async" />
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jakearchibald - I'd thought about having these images get skipped when prerendering. Right now because they're sent as static HTML, they kick of 4 requests that extend our "load" time out by quite a bit (which is more of a metrics issue, but still). Thoughts?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only metrics I care about are "time until useful pixels are on the screen" and "time until the user can perform the first interaction".

If one of Google's many acronyms reports the wrong time for those, it's their bug, not our's 😄 (we should report it though).

@jakearchibald
Copy link
Collaborator

@developit that bug sounded like a nightmare to track down. Thanks for working through it.

Copy link
Collaborator

@jakearchibald jakearchibald left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

main.css contains a lot of duplicated stuff from the inlined CSS. It might be better/simpler to just inline main.css and not include it as an external file.

Also, the <noscript> stuff is pointless for us.

@developit
Copy link
Collaborator Author

@jakearchibald I put some time into improving Critters, and it now strips inlined critical styles out of the lazy-loaded noncritical stylesheets. I also added a threshold option so it doesn't do something silly like lazy-load 300b of CSS and instead just inlines the rest. With those in-place, I think everything is optimal here:

screen shot 2018-11-03 at 8 53 34 pm

Our index.html is 6.84kB with 100% of the CSS and fonts inlined to keep layout stable.

@developit
Copy link
Collaborator Author

Alright, fairly happy with this now:
https://www.webpagetest.org/result/181104_90_329591be7c50cebf5e4564c5494638a2/

src/index.ts Outdated
await import('@webcomponents/custom-elements');
}

if (!('customElements' in self)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is fragile (as in, it needs to be written in this way else webpack or whatever doesn't do the right thing), add a comment stating that, else I'll probably break it again 😀

Copy link
Collaborator

@jakearchibald jakearchibald left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One minor nit, but this looks great.

It'll regress a bit when I add CSS to hide non-interactive bits. We may want to adapt our approach a bit to cater for that. We may want to inline the logo, and maybe even the initial bundle

We can experiment more once the UI branches have landed.

@jakearchibald
Copy link
Collaborator

jakearchibald commented Nov 4, 2018

In fact, the more I stare at the webpagetest results, we could save half a second by inlining the first-interaction JS. We'll look at that in another branch though.

@googlebot
Copy link

So there's good news and bad news.

👍 The good news is that everyone that needs to sign a CLA (the pull request submitter and all commit authors) have done so. Everything is all good there.

😕 The bad news is that it appears that one or more commits were authored or co-authored by someone other than the pull request submitter. We need to confirm that all authors are ok with their commits being contributed to this project. Please have them confirm that here in the pull request.

Note to project maintainer: This is a terminal state, meaning the cla/google commit status will not change from this state. It's up to you to confirm consent of all the commit author(s), set the cla label to yes (if enabled on your project), and then merge this pull request when appropriate.

@jakearchibald jakearchibald changed the base branch from master to service-worker November 8, 2018 12:01
@jakearchibald
Copy link
Collaborator

I've rebased this onto the service worker branch for now

@jakearchibald jakearchibald changed the base branch from service-worker to master November 8, 2018 12:21
@jakearchibald
Copy link
Collaborator

lol aaaaand back to master

@jakearchibald
Copy link
Collaborator

https://www.webpagetest.org/result/181108_DQ_daaae433b6dd49e4cd0dea126b3e1983/ here's the result of inlining. It seems to regress performance.

@jakearchibald
Copy link
Collaborator

Doesn't actually seem like a regression vs not-inlining https://www.webpagetest.org/result/181108_P7_99f8c7b040cff1b713ce6e11d7efbdde/#run3. Render time stays the same, but it brings interaction time down by a few tenths.

@jakearchibald jakearchibald merged commit 7616d33 into master Nov 9, 2018
@jakearchibald jakearchibald deleted the startup-optimizations branch November 9, 2018 16:01
alisaitbilgi pushed a commit to alisaitbilgi/squoosh that referenced this pull request Feb 19, 2019
* Startup optimisations

* I hate this file

* Inline main script

* Reverting change to do a fairer perf comparison

* Inlining again. Weeeeee!

* Lockfile
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.

Load main script in head, deferred
5 participants