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

Experiment of initializing to pre-rendered DOM #104

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

samuli
Copy link

@samuli samuli commented Nov 23, 2018

This is an experiment of initializing a bucklescript-tea application to a pre-rendered DOM.

The app is provided with a flag at startup that tells if the parent node already includes rendered HTML which should be preserved.

Apart from supplying the application with the flag, the only changes are:

  • initializing the priorRenderedVdom either to empty or to vdom of initial model state
  • skipping the emptying of the parent node

The page does not flash when bucklescript-tea is inited. Chrome developer-tools claims that the DOM is preserved and does not get re-rendered at page load. Page scroll position is also preserved.

Am I missing something or could this work?

Minimal example for testing:
https://github.com/samuli/bucklescript-tea-server-render

@OvermindDL1
Copy link
Owner

This was an entirely planned use-case! I just never got around to it. ^.^

I was initially thinking of another pattern to do it but this hydrate way is interesting, and since undefined is false then it should be backwards compatible with the current javascript API (though would change the ocaml API, but trivially so).

In addition to this might need to flesh out the vdom->htmlString converter function, the existing one was just for debugging so it no doubt could use some work. ^.^;

Also on the ref [view initModel] in the if hydrate = true then branch it probably needs to be recursively gone over to 'clean up' of dynamic elements like listeners so the HTML will get properly populated when the initial run is executed. Such a cleanup function would belong in the vdom module itself of course.

This could grow to a fairly useful PR if you are wanting to do it. :-)

@OvermindDL1
Copy link
Owner

For note, the original pattern I was thinking of was a new vdom function to merge a vdom into an "existing" DOM, instead of comparing two vdom's, and call the one to merge instead of diff/merge instead. This may still be overall more generically useful and more performant, but it is a lot more code to write.

@samuli
Copy link
Author

samuli commented Nov 27, 2018

Also on the ref [view initModel] in the if hydrate = true then branch it probably needs to be recursively gone over to 'clean up' of dynamic elements like listeners so the HTML will get properly populated when the initial run is executed.

Good point. I didn't think about this... Any idea how we can remove the "pre-rendered" listeners without having a reference to the actual listener (which is needed by removeEventListener)?

I can only think of the following workarounds:

  • Put an extra attribute to the parent node and all child nodes that have dynamic properties. If needed, TEA would traverse the parent and replace all flagged child nodes.
  • Require users to override addEventListener and pass the listeners to TEA at startup
  • Use getEventListeners (non-standard)

I think only the first alternative could be somewhat usable. But would it have a realistic use-case? If TEA is a component that communicates with some other part, you could have, say, a click handler that would work before TEA is inited.

Or could hydrate be a feature that is known and documented to work with only static markup?

I was initially thinking of another pattern to do it but this hydrate way is interesting, and since undefined is false then it should be backwards compatible with the current javascript API (though would change the ocaml API, but trivially so).

I also considered having "hydrated" versions of program types (standardProgramHydrated, beginnerProgramHydrated...).

@OvermindDL1
Copy link
Owner

OvermindDL1 commented Nov 27, 2018

Good point. I didn't think about this... Any idea how we can remove the "pre-rendered" listeners without having a reference to the actual listener (which is needed by removeEventListener)?

Reasons like this a dedicated 'merge' instead of patch call in the vdom might be more useful, but that is why recursively iterating through the vdom node as it stands is good as you can then just remove the listeners, that way when the first patch happens then it will patch the no-listeners vdom to the listeners vdom. 'Should' be a relatively simple function, I could probably whip it up into the vdom.ml file today if you want to merge it in and use it if it would help?

Put an extra attribute to the parent node and all child nodes that have dynamic properties. If needed, TEA would traverse the parent and replace all flagged child nodes.
Require users to override addEventListener and pass the listeners to TEA at startup
Use getEventListeners (non-standard)

Eh, none of those are really necessary, just 'sanitize' the first view call to get rid of them instead, much easier. :-)

Or could hydrate be a feature that is known and documented to work with only static markup?

This is what I'd do regardless, you can't assume a lot of things, and this is also why I think doing an in-dom patch instead of a vdom->vdom patch might be better for the first iteration as it would 'fixup' the DOM regardless, that way the person making it could even have html that doesn't exist in there like a "Please wait" message or something, it would be all around more useful... I think I'm leaning more to that pattern... I'll see if I can add it to the vdom function sometime today and that way you could then make use of it in your patch to use it instead.

I also considered having "hydrated" versions of program types (standardProgramHydrated, beginnerProgramHydrated...).

Except that constrains it to being enforced by program instead of usage, which means something could either build inline or it could build anew. I'm thinking it might be better to just 'transform' all existing ones to an inline merge instead, so it would just 'always' do that. It would have no functionality changes for the existing usage but it would just transparently add the capability for upgrading static HTML. :-)

@samuli
Copy link
Author

samuli commented Nov 27, 2018

think I'm leaning more to that pattern... I'll see if I can add it to the vdom function sometime today and that way you could then make use of it in your patch to use it instead.

Yes, that would be good. I'm still a bit confused how the removal of the pre-rendered events could be done without re-rendering the nodes...

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