Skip to content
This repository has been archived by the owner on Apr 24, 2020. It is now read-only.

Allow routes to be rendered server side #2

Open
ctdio opened this issue Apr 22, 2017 · 6 comments
Open

Allow routes to be rendered server side #2

ctdio opened this issue Apr 22, 2017 · 6 comments

Comments

@ctdio
Copy link
Owner

ctdio commented Apr 22, 2017

At the moment, the router does not allow for routes to be rendered server side. For this to be able to happen, some tweaks need to be made with how components are rendered by the router.

Also, since the input that the router would receive after a server side render would be a plain javascript object (stripped of all functions), a fresh set of routes would probably have to be passed into the router so that it can rebuild it's internal path router with proper components that can be rendered.

@mauricionr
Copy link

👍

@libeanim
Copy link

libeanim commented Oct 7, 2017

Hi @charlieduong94,

what do you think of the following workaround?

  1. Lasso is used to render a template file on the server with express.
// server.js

app.get('/*', function(req, res) {
  res.marko(
    require('./src/components/template.marko'),
    { initialRoute: req.path }
  );
}
  1. An include-tag is used to add a specific component dynamically according to the requested path on the server. (see template.marko)
  2. in the onMount function (which gets executed on the client) the router is initialized + rendered asynchronously and then replaces the content in the dom accordingly. (see template.marko)
// src/components/template.marko

import routes from "../routes";

class {
  onMount() {
    const appComponent = this.getEl("app");

    let initialRoute = "/";
    if(window && window.location){
      initialRoute = window.location.pathname;
    }
    console.log("initialRoute", initialRoute);

    Router.render({
      routes: routes,
      initialRoute,
      },
      (err, result) => {
        result.replaceChildrenOf(appComponent);
      }
    );
}

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>test</title>
    <lasso-head/>
  </head>
  <body>
    <div no-update key="app">
      <include(getComponentByRoute(routes, input.initialRoute || "/"),
               getParamsFromUrl(routes, input.initialRoute)) />
    </div>
    <lasso-body/>
  </body>
</html>

Both getComponentByRoute and getParamsFromUrl are executed on the server.

  • getComponentByRoute gets the same route-data as the router on the client and just returns the correct component for this route.
  • getParamsFromUrl transforms the url (using route data for nested components) to get the parameters which are then injected as input into component loaded via include.

In this case the page is completely rendered on the server and the client router updates the page as soon as it's loaded.

Are there any problems I didn't see or is there a better solution?

Addition: In this example the initial route on the client contains only the path name (location.pathname), in the real world one needs to add the search + hash

@ctdio
Copy link
Owner Author

ctdio commented Oct 8, 2017

Hello @libeanim, interesting workaround! Your solution seems pretty sound to me. Have you tried to get a simple proof of concept working?

@libeanim
Copy link

libeanim commented Oct 8, 2017

@charlieduong94 thanks for your quick response.

I quickly set up a demo. Didn't parse any url parameters (so no getParamsFromUrl) or placeholder paths (e.g. /user/:userid) tho, but I guess it shouldn't be a problem to implement that, as it is done somewhere in the router anyways.

@ctdio
Copy link
Owner Author

ctdio commented Oct 8, 2017

👍 awesome! The only minor issue I can see happening is that components that perform animations on initial render or perform any sort of setup during their onMount phase may see some "flickering" from router component remounting on the page.

Ideally, I would like to have the router transparently rebuild it's internal state without having to perform a full rerender. Perhaps this can be done by simply exposing a way for the router to "refresh" it's routes upon the page's onMount (that's something I'll try to play around with). Other than that, I think this is still a pretty good solution for server side rendering. Nice job @libeanim

@libeanim
Copy link

libeanim commented Oct 9, 2017

Thanks!

Yeah you are right this is a problem, the component shouldn't be rerendered/mounted twice. Cool, I'm curious to see your final solution! Keep us in the loop 😃

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

No branches or pull requests

3 participants