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

ssr/hydration: keep markup created by custom elements in the dom #764

Closed
wants to merge 1 commit into from

Conversation

naltatis
Copy link

@naltatis naltatis commented Oct 2, 2018

fixes the issue, that hyperapp throws out existing markup that is created by custom elements before the app is initialized.

In which scenarios does this matter?

shadow dom polyfill
Custom element code gets executed before hyperapp has hydrated the ssr markup. In browsers that don't support native shadow dom yet, its common to write the CEs private markup into the normal/light dom.

custom element ssr
There is no standard way to server render custom elements, but serveral frameworks or tools (skates, stencil, ssi) offer proprietary ways to prepolulate CE markup on the server side to improve seo and webperf.

I've added a condition to skip the existing-markup-cleaning-routine for custom elements in patch() in the first isRecycling run. This way existing markup that hyperapp did not expect stays intact and CE children that are explicitly stated in the view still get hydrated correctly.

I first tried to do the skipping in recycleElement as @zaceno suggested in #757. But by doing this, hyperapp would create double markup for CE child elements that are defined in the view instead of hydrating the existing elements (see testcases).

@jorgebucaran jorgebucaran added this to the Future milestone Oct 2, 2018
@jorgebucaran
Copy link
Owner

jorgebucaran commented Oct 2, 2018

Thank you, @naltatis!

Getting this merged and published is going to take me some time, but I promise to have a look at it soon after V2 is out. I can't make any promises, but I may accept this patch sooner than that and publish a new V1 minor with it. However, that is no guarantee it will be ported to V2—at least not at first.

I appreciate your patience in the meantime! 🙏

@naltatis
Copy link
Author

naltatis commented Oct 2, 2018

@jorgebucaran Thanks for your response.

I indeed haven't looked into the changes that are in V2. Do you think its worth checking the described usecases against the current V2 as well and create another PR for that if needed? Or do you first want to take your time with this one?

@jorgebucaran jorgebucaran added bug Something isn't working Fix and removed Fix labels Oct 19, 2018
@jorgebucaran
Copy link
Owner

This PR shows that things get a bit tricky when hydrating custom elements and adds extra unhappy bytes to Hyperapp.

However, V2's latest beta changes the internal DOM mounting mechanism, affecting how hydration works in a way that allows us to remove built-in hydration without changing Hyperapp's surface API.

Removing built-in hydration means you'd need to import a hydrate function from a different package and pass the result to the app function like so:

import { h, app } from "hyperapp"
import { hydrate } from "@hyperapp/rainbows"

app({ 
  // ...,
  node: hydrate(document.getElementById("app"))
})

allowing us to implement a more sophisticated hydration strategy that supports custom elements.

Thoughts?

@naltatis
Copy link
Author

naltatis commented May 3, 2019

I didn't follow the V2 changes closely, but extracting the hydration code to its own opt-in package sounds good.

@jorgebucaran
Copy link
Owner

@naltatis Cool. If we extract hydration to an external package, I'll come back to see what you did here. But that will be after shipping V2. Also closing as I won't be merging these changes into core this time.

Thank you!

@ersinakinci
Copy link

Wondering if you've had the time to look at this again, @jorgebucaran, or if there's now a way to hydrate? Thanks!

@ersinakinci
Copy link

For people coming from a web search, I spoke with @jorgebucaran and Hyperapp already supports hydration. Simply mount the app on a DOM tree that's identical to the DOM that gets generated by Hyperapp, and the library will take over. This PR isn't about hydration in general, it's a specific problem involving custom elements.

There's no documentation on this as of writing, but @jorgebucaran told me this in Slack:

  • Hyperapp will try to hydrate child nodes instead of throwing away your server-side rendered content. Hydration recycles existing DOM (usually from server-side rendering) rather than create new elements.
  • Hyperapp works transparently with SSR and pre-rendered HTML, enabling SEO optimization and improving your sites time-to-interactive. The process consists of serving a fully pre-rendered page together with your application.
  • Then instead of throwing away the server-rendered markdown, we'll turn your DOM nodes into an interactive application.
  • Hyperapp expects server side rendered content to be identical between the server and the client. You should treat mismatches as bugs and fix them.

If you serve:

<body><div><h1>ok</h1></div></body>

then make sure to:

app({ 
  ..., 
  view: () => h("div", {}, [h("h1", {}, ["ok"])]) 
})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants