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

Missing Content message flashes on initial load #42

Closed
dillonkearns opened this issue Jan 9, 2020 · 4 comments · Fixed by #48
Closed

Missing Content message flashes on initial load #42

dillonkearns opened this issue Jan 9, 2020 · 4 comments · Fixed by #48

Comments

@dillonkearns
Copy link
Owner

dillonkearns commented Jan 9, 2020

Here are some ideas on how to approach this.

What's happening now

  1. Browser loads and renders the HTML
  2. Since the <script> tag that includes the Elm bundle is in the <head>, and doesn't have a defer on it, I believe it will block the rest of the HTML from rendering? (need to confirm this)
  3. As the page is loading, we see this message briefly as it fetches the content.json file, which contains the body of our content, as well as any StaticHttp data for the page:
    { title = "elm-pages error", body = Html.text "Missing content" }
  4. The page fully loads and displays correctly

Possible solution

  1. Render the HTML content immediately (ensure that the Elm bundle doesn't block HTML rendering, but ideally it should also be fetched concurrently as the HTML is rendered... possibly using a <script defer ...>?).
  2. Pass in some flags to the low-level elm-pages init that contains already-fetched content.json data (could either hardcode that data into the generated HTML file, but probably best and most performant to fetch it once the HTML is rendered, and then callback once it's fetched to init the elm app with that data as a flag).
  3. Now, when the Elm app takes over, it has all the data it needs so the "Missing content" message won't flash because the content is present on init from the flag

Old solution idea

What could happen instead (this is just one solution path) I thought this would be a good approach, but thought of the simpler solution above

  1. Render the HTML content immediately (ensure that the Elm bundle doesn't block HTML rendering, but ideally it should also be fetched concurrently as the HTML is rendered... possibly using a <script defer ...>?).
  2. Render the Elm view off in an invisible <div> (will this cause rendering to differ at all? Probably not because it's Elm, right, so it should render reliably even off in an invisible div?).
  3. Listen for the Elm view function in the invisible <div> to be called... can use the MutationObserver technique similar to how elm-pages currently listens for page changes in order to know when the URL has changed

    elm-pages/index.js

    Lines 72 to 84 in 5754bfb

    function observeUrlChanges(
    /** @type {MutationRecord[]} */ mutationList,
    /** @type {MutationObserver} */ _theObserver
    ) {
    for (let mutation of mutationList) {
    if (
    mutation.type === "attributes" &&
    mutation.attributeName === "data-url"
    ) {
    setupLinkPrefetchingHelp();
    }
    }
    }
  4. Once the view has been rendered, remove the visible, server-rendered view, and show the hidden, client-rendered view (will this cause any reflow or other kinds of jank? Is there a way to prevent that?)
@dillonkearns
Copy link
Owner Author

Prototype details

Here's a prototype where I made the following changes as a proof-of-concept:

  • Use <script defer ...> tag to load the main Elm bundle
  • Hardcode in data for the /blog/introducing-elm-pages/ route (the only route in this prototype version for simplicity). So all the data is present when init is called (no need for an HTTP request to fetch /blog/introducing-elm-pages/content.json).

Testing the prototype

The prototype only has a single URL: https://5e1fe334cd8a9f0008d3412a--elm-pages.netlify.com/blog/introducing-elm-pages/

You can run through manual tests with it here. In Chrome, I recommend the following settings:

  • Go to the developer tools and find "Application > Service Workers". Check "Bypass for Network" and "Update on Reload" (see screenshot below) Image 2020-01-15 at 9 45 46 PM

TODO

  • Add a counter that increments based on a subscription to the current time to 1) make it more clear when the hydrated Elm app takes over the DOM while you're manually testing, and 2) see what happens when rehydrating an app that has a Model state. Because puppeteer currently takes a snapshot after allowing the user's update function to run as many times as needed... I suspect the counter will start out at well above 0. But we could just prevent the user's update function from ever being called during the pre-rendering phase to prevent this, might make sense and be pretty straightforward.

@icidasset
Copy link
Contributor

I've been thinking a bit about this, and I think I have another solution.

  1. We don't need to load Elm immediately, since the html file already has the correct content
  2. We load the content.json file in javascript.
  3. When the content has been loaded, load up Elm with the content as a flag. This'll replace the static html.

My only worry here is, will users see a "flash" when the content is being replaced?
What do you think, is this a viable solution?

@dillonkearns
Copy link
Owner Author

Hello @icidasset, thanks for chiming in! That's actually exactly what's happening in the prototype I have here: https://5e20f901469a7c0008528294--elm-pages.netlify.com/blog/introducing-elm-pages/

You can see the moment when Elm takes over is when the counter starts going up (if you throttle your connection, you can see it even better). There was some discussion in the Elm slack by the way about the idea of not calling the user's update function during the pre-render phase at all, which would prevent the number in the pre-rendered HTML from showing up as a 5 (it would be a 0 instead), and then starting out from 0 when Elm takes over.

If you pull up that link and open up your favorite browser performance inspection tools, I'd love to hear what you think of the current state! It seems pretty smooth other than a small flash of the image when Elm takes over (I'm sure there are other blemishes for different types of content like animations, for example, since Elm is actually considering the DOM to be brand new rather than adopting the old one).

@icidasset
Copy link
Contributor

Oh, for real? Great 😄

There was some discussion in the Elm slack by the way about the idea of not calling the user's update function during the pre-render phase at all

Yeah, that seems like a great idea, could possibly prevent some weird situations.

It seems pretty smooth other than a small flash of the image when Elm takes over

It does feel pretty smooth yeah!
Great work @dillonkearns 👏

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 a pull request may close this issue.

2 participants