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

Avoid unnecessary rerenders at init #512

Merged
merged 2 commits into from
Jan 26, 2025

Conversation

lydell
Copy link
Contributor

@lydell lydell commented Jan 26, 2025

When an Elm app boots, it “virtualizes” existing DOM and diffs against that on the first render. If the existing DOM was similar to the return value of the first view, not much of the DOM needs to be updated.

https://github.com/elm/browser/blob/1.0.2/src/Elm/Kernel/Browser.js#L78-L85

Most Elm apps render into empty or near-empty nodes, so that is rarely relevant. But elm-pages server-side renders views, so it can take advantage of this feature!

However, there is currently a mismatch between the server rendered HTML and what the Elm view produces. The diff at init looks like this:

  1. <body> vs <body>
  2. <div data-url> vs <div data-url>
  3. <main> vs <div id="elm-pages-announcer">
  4. ❌ nothing vs <main>

Since <div id="elm-pages-announcer"> is missing in the server-side rendered HTML, it throws off the diffing forcing Elm to basically rerender the entire page.

Then, to make things more complicated, whitespace matters too. When hand-writing HTML, we often put newlines and indentation among elements to make things more readable. However, that results in text nodes with only whitespace! Usually they don’t matter at all, but they do matter when virtualizing DOM. Elm can’t know which text nodes are important and which ones aren’t. <b>one</b> <b>two</b> has a whitespace-only text node which is important – without it the text renders as “onetwo” instead of “one two”. So just adding <div id="elm-pages-announcer"> to the server-side rendered HTML isn’t enough – there are some whitespace text nodes that throw things off as well.

This PR:

  • Adds <div id="elm-pages-announcer"> to the server-side rendered HTML.
  • Removes whitespace text nodes in <body>.
  • Adds comments about this.

I found this when working on my fork of elm/virtual-dom. Which brings me to … this PR only avoids unnecessary rerenders in theory. Because as soon as you wrap something in Html.map or Html.Lazy.lazy, everything inside them is going to be rendered anyway. elm-pages does List.map (Html.map UserMsg) body to the user’s view, so basically the whole page is wrapped in Html.map. The reason this causes a rerender, is because elm/virtual-dom’s virtualize function cannot know where map nodes should be, and the diffing algorithm bails when a map node is diffed with a regular node. My fork of elm/virtual-dom handles this, though, so when that is finished, this PR will have value for those who choose to use my fork!

My fork isn’t done yet, though, but I thought I’d make this PR while I still remember all the details.

Copy link

netlify bot commented Jan 26, 2025

Deploy Preview for elm-pages-todos canceled.

Name Link
🔨 Latest commit 76bb825
🔍 Latest deploy log https://app.netlify.com/sites/elm-pages-todos/deploys/679660d74e215d000808f98c

@dillonkearns dillonkearns merged commit db197dc into dillonkearns:master Jan 26, 2025
9 checks passed
@dillonkearns
Copy link
Owner

This is fantastic, thank you @lydell! I went ahead and merged it because the changes you've made so far look safe in that they won't make things any worse, even if it doesn't fully complete the work of making it diffable. Thank you for taking the time to work on this!

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.

2 participants