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

support server side rendering #3955

Closed
jay8t6 opened this issue Sep 14, 2016 · 27 comments
Closed

support server side rendering #3955

jay8t6 opened this issue Sep 14, 2016 · 27 comments

Comments

@jay8t6
Copy link

jay8t6 commented Sep 14, 2016

I know this is not currently in the roadmap, but it would be great if you guys can support server side rendering like riot.js

@ebidel
Copy link
Contributor

ebidel commented Sep 15, 2016

Check out:

https://pimterry.github.io/server-components/

https://scotch.io/tutorials/server-side-web-components-how-and-why

On Wed, Sep 14, 2016 at 8:02 AM jay8t6 notifications@github.com wrote:

I know this is not currently in the roadmap, but it would be great if you
guys can support server side rendering like riot.js


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
#3955, or mute the thread
https://github.com/notifications/unsubscribe-auth/AAOigHBnUWG7NxQ0QKuoJtySR7buNwmpks5qqAyIgaJpZM4J84H5
.

@kevinSuttle
Copy link

Wondering if that would speed up adoption.

@vpusher
Copy link

vpusher commented Oct 11, 2016

The real challenges about server side rendering with web components are:

  1. Backend: To render the component on the server side (this may need a bunch of help from the Polymer team to ensure the Polymer runtime runs properly in a server environment).
  2. Frontend: Catch the rendered output (i.e. rendered template) within the page so the web component take control on it and re-render it.

Even if (1) seems on its way for native Web Components, (2) could be very hard to handle for Polymer, isn't it ?

I'm wondering to integrate Polymer (or more globally Web Components) in our app but server side rendering lack might be a major blocking issue.

Going deeper in the reflexion, this seems a bit paradoxal for the Web Components spec itself that want to bring us back to use "native" elements (HTML first, ...) while those "native" elements needs to be pre-rendered for performance, SEO, ... -_-'

@IchordeDionysos
Copy link

IchordeDionysos commented Nov 14, 2016

In the last couple day after I watched @jakearchibald's talk for the Chrome Dev Summit, I thought a lot about how to improve initial page load with Polymer (using Server Side rendering), but I couldn't find a convincing solution without rewriting Polymer. So I just came up with that idea:

How about you can tell Polymer that you already piped a Polymer page throw Chrome V8 on the server?

window.Polymer = {
  dom: 'shadow',
  serverRendered: true
};

So Polymer knows, that you already rendered all elements and run all ready/attached callbacks on the server, so Polymer skips them and only adds the EventListeners for property change events to make all necessary runtime changes after running the initial Javascript.

I hope this is possible somehow and I know that I didn't thought over all possible scenarios and features Polymer has, it's only a concept in my head. But I think this should be possible somehow.

This should boost performance on initial page load.

@IchordeDionysos
Copy link

Additional opinion on what @vpusher mentioned:

  1. Thus should be no problem. Just run Chrome V8 e.g. via Python on the whole page and Polymer does all the rendering stuff.
    The second problem should be resolved by the solution I mentioned above.

I would like to start a discussion about this topic.

@treshugart
Copy link

treshugart commented Nov 14, 2016

I'm not a Polymer dev but having done quite a bit of experimentation around this with web components, I may be able to add some insight.

Infrastructure for rendering DOM on the server is the easy part. There's two hard parts about this that come to mind:

  1. how you serialise the shadow roots so they can be rehydrated on the client
  2. rehydrating on the client

The first part isn't as easy as it sounds because you need to represent the shadow root in HTML. Once you do that, you need to also represent the slotted content and the state in which the slots are (displaying slotted content or default content). This also needs to be done in a way where styling works both inside and outside of the shadow root, depending on what you need the server rendered content for.

Rehydrating is an easier task, but requires a lot of work on the client and some potential reflows. The declared shadow root must be imperatively initialised and templated. Then all slotted nodes must be moved to light DOM so they can be slotted.

I'd venture to say that since you're doing that much work already on the client, that you may as well just render in the client. You have to do this anyway and would just be adding more work for the client to do by having custom distribution logic on connected.

In doing SSR, you'd also be delivering more page weight to the client than you would be if it was just your CE definitions.

I'm interested in exploring some alternate approaches here, if anyone has any. Every time I've approached it, it seems to lead down the path described above. Either that, or it becomes very coupled to the framework. Even then, if you're using shadow DOM, a lot of the same principles apply.

@IchordeDionysos
Copy link

This is no problem if you use 'shady dom', but that is not the target. I didn't thought of shadow DOM as I suggested this. As I understood it, to set up all shadow roots you have to run Javascript first, right?
The nearly only reason why I want to have SSR is because I can have content fast, even faster if you only fetch the content from the network and then re-render client side. (Demo of Github Issues in @jakearchibalds's Chrome Dev Summit talk)
Maybe Shadow DOM should have some kind of Markup you can declare that Chrome can add a shadow root to the page earlier than every developer has the ability to do, because as a developer of a website you have to wait until the page is downloaded and Javascript has run.

@jfrazzano
Copy link

The only issue is the on-connected “starters-pistol”, which represents the the first time an element, module, or full App can inspect its children, measure, make adjustments, share properties, etc. So come of the more commonplace pre-render stuff is made more complex by the need to register an element. Elements can, of course, be pre-registered as object apis, although i have no idea how that affects a polymer set of tools.

On Nov 14, 2016, at 5:11 PM, Dennis Kugelmann notifications@github.com wrote:

content

@treshugart
Copy link

treshugart commented Nov 15, 2016

@IchordeDionysos

As I understood it, to set up all shadow roots you have to run Javascript first, right?

Currently in native, yes.

Maybe Shadow DOM should have some kind of Markup you can declare that Chrome can add a shadow root to the page

There is an issue relating to this in the w3c/webcomponents repo, but it was closed due to no concrete proposals. I definitely think it's worth revisiting at a spec level, and experimenting with at a polyfill level to see what's possible. Without native support, though, I think PRPL will get you a lot further in terms of performance (even with native support).

@ergo
Copy link

ergo commented Mar 28, 2017

Wouldn't SSR work best with "view" components that could be created without shadow dom?

@IchordeDionysos
Copy link

Currently yes, because Shadow DOM can only be applied imperatively after the page is loaded using JavaScript and not declaratively using HTML. The spec is missing there a part. It would be great if the browser would create shadow dom out of a streamed HTML file. This would solve so many problems with SSR.

@baocang
Copy link

baocang commented Apr 1, 2017

SSR +1

@TimvdLippe
Copy link
Contributor

Just want to chime in. Last month I gave a presentation on exactly this topic of first load performance. In general, SSR is not required, but is usually the solution applied when the first paint is significantly delayed due to big scripts being parsed and executed.

Instead of rendering on the server, I propose(d) to serve a fully working HTML response and then make use of custom elements upgrade mechanism to load all other components. The results for our website (https://symposium.ch.tudelft.nl/2017/) were spectacular, in the sense that the first paint and first meaningful interaction are very very fast. After the first paint is done, it loads the other dependencies with the HTML imports and executes the scripts of Polymer. Then the custom elements are upgraded (which use shadow dom) to make sure that styles are not leaking and the rest of the website is functional.

You can view my slides at https://timvdlippe.github.io/presentation/browser-as-our-framework/#0 I am not sure how useful they are without me talking. After slide 12, I start my section on load performance. Slide 18 shows the problem we encountered on 3G. Slide 20 shows a direct comparison to before (left) and after (right). On 3G that is 12 seconds -> 6 seconds (e.g. 50% off). In essence, the index.html is scaffolded with an initial fully working response, as shown on slide 24.

I hope this information is useful and show why SSR might not be required. Looking forward to your response!

@TimWillis
Copy link

SSR +1

@treshugart
Copy link

treshugart commented May 31, 2017

We've started working on SSR over here: https://github.com/skatejs/ssr. It's not Skate specific, however, Polymer likely uses more DOM interfaces than Undom implements (which it uses currently behind the scenes. Skate uses a very minimal subset of the DOM APIs while still being pure web components, which is why it works well. It's likely to work with Vanilla custom elements / shadow DOM if you limited yourself to a small subset or used something like Preact to render the element.

JSDOM support is in progress over here jsdom/jsdom#1872, which would enable broader coverage of the DOM APIs.

The roadmap for our SSR library is to eventually be able to use any server-DOM impl (Undom, JSDOM, etc) and focus solely on serialisation / rehydration.

@TimvdLippe
Copy link
Contributor

Closing per above comment. Make sure you check out @treshugart talk at the summit! https://summit.polymer-project.org/schedule called "SSR Web Components"

@KiaraGrouwstra
Copy link

From the talks it seemed we should do SSR using Rendertron, e.g. with Firebase functions.

@ghost
Copy link

ghost commented Oct 4, 2017

Is the (main) hurdle here the fact that Polymer/Web Components don't have a virtual DOM?

@ergo
Copy link

ergo commented Oct 4, 2017

No, you can use virtual DOM with polymer elements.

@ghost
Copy link

ghost commented Oct 4, 2017

Good to know, thanks.

@arthurevans
Copy link

@KSuttle-cng and just to clarify—I don't think there's any connection between virtual DOM and SSR.

The issue with SSR and shadow DOM is that you can't serialize shadow DOM into HTML markup—right now, a shadow root can only be created using JavaScript. Rendertron works around this by using the shady DOM/shady CSS polyfills, which simulate shadow trees using regular DOM trees + JS patching and assorted CSS tricks. The result is a tree that can be serialized and sent down to the browser.

Virtual DOM is a strategy for updating the DOM tree after the browser has created it. The server is still sending markup down to the browser, and the browser is parsing it into a DOM tree. JavaScript code (react-dom or preact or whatever) maintains the "virtual DOM" and uses it to determine how to update the "real" DOM—the nodes the browser actually displays.

@yorrd
Copy link

yorrd commented Mar 17, 2018

@TimvdLippe I don't follow. The link you closed this by does not apply to Polymer, skatejs/ssr only works for vanilla HTMLElement. I haven't figured out why, but Polymer will not compile on the server using skatejs.

@TimvdLippe
Copy link
Contributor

@yorrd Polymer components are web components and should therefore "just" work with Skate SSR. If you are encountering any issues, I advise you to open an issue over there to investigate and resolve your problem.

@yorrd
Copy link

yorrd commented Mar 18, 2018

@TimvdLippe thanks for your stand on this. I think I'll be waiting for the smaller lit-html base class. The problems I encountered had to do with the dom and window element including the event infrastructure not being available on the server. I started emulating a browser environment using the window node package, but there were still loads of issues with missing functionality. If it turns out that even the small base class without the property binding dependencies doesn't work, I'll write an extensive report. Thanks

@trusktr
Copy link

trusktr commented Nov 11, 2018

Polymer likely uses more DOM interfaces than Undom implements

The problems I encountered had to do with the dom and window element including the event infrastructure not being available on the server

Running Electron in headless mode works great. All Chrome APIs are available there. You can run the app in Electron, then get the innerHTML from it and send it to the client.

The best part about it is that you can run both your front end and back end tests all in one setup (i.e. you have a Node.js backend which can use the Node.js APIs exposed in Electron).

The new Google Carlo (official Chrome + Node.js integration) project might be a nice alternative soon too.

@trusktr
Copy link

trusktr commented Nov 12, 2018

Polymer components are web components and should therefore "just" work with Skate SSR

It won't just work because if a Polymer element tries to create its own shadow root, it may fail when there's already one created by the client-side Skate SSR script. If attachShadow is patched so that it returns an existing root instead of failing (because v1 shadow DOM makes it throw an error) then that'd work. Otherwise, Polymer would need to internally use an API that return an existing shadow root if one already exists instead of using attachShadow directly (not sure if Polymer already does that).

Basically, if a web component can detect and use an existing shadow root when there is one, and otherwise make a new one, then it should theoretically work with Skate SSR.

Now, getting Polymer to run in an undom or jsdom environment, that's a another issue (see the headless browser suggestions above instead of using undom or jsdom).

@trusktr
Copy link

trusktr commented Nov 12, 2018

I just did a quick search for attachShadow in Polymer. Here's where it uses attachShadow.

Skate SSR (currently) makes a shadow root on a rehydrated element with mode: 'open', so this means the conditional check for if (!this.shadowRoot) in Polymer will work, and the Polymer component will successfully re-use the shadow root created by Skate SSR.

So, it should "just" work.

There's some things to consider:

  • this assumes shadow roots made by Skate SSR are always created with "open" mode.
  • If in the future Skate JS makes "closed" shadow roots based on what the Web Component specified (if perhaps Skate SSR were to observe this by monkey patching attachShadow), then that if (!this.shadowRoot) check will not work as expected.
    • In this case, we'd need to override the Polymer _attachDom method to account for this and use an API provided by Skate SSR to work around the fact that "closed" roots are not exposed on this.shadowRoot.

TLDR, if you always use "open" shadow roots, it should work. The hardest part is getting the code running on the backend, not the Shadow DOM stuff.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests