Server-Side Rendering Isn't Enough

Matthew Phillips

@matthewcp

Terms

  • Shared codebase
  • Isomorphic
  • Universal

Why bother

Perceived performance: no one likes staring at a spinner.

This sucks

SEO: if you care about that sort of thing, it helps. Not every bot is Googlebot.

BLING BLING: Amazon reports that conversion increased by 1% for every 100ms improvement.

The state of Server Rendering

Everything should be server rendered

Requirements

  • Rendering speed
  • Only includes the assets needed (CSS and JavaScript)
  • Prevents unnecessary requests in the client

Performance

  • Shared router
  • Asynchronous rendering
  • Fast development experience with hot module swapping

Maintainability

Rendering Performance

Headless Browser

PhantomJS

  • Consumed a lot of memory
  • Needed pooling
  • Very fast

Virtual DOMS

  • Run the same code on the client and server
  • Run within a single Node context
  • Rendering is usually synchronous

can-ssr's vdom

Looks like a real DOM, only the basics

Demo compatibility

Minimizing request size

/cart

Title

Traditional method

  1. Initially unstyled
  2. Main, site-wide style is loaded
  3. Page specific style is loaded progressively.

CSS loaded in JavaScript

/cart

Title

with server template

  1. Initially partially styled; main CSS is included, most of the page-specific CSS.
  2. Rest of page-specific styles are added.

adding css manually

/cart

Title

With Donejs

  1. All styles needed for the page are included directly in the head.
    • And only the styles needed for the page.

can-ssr does it for you

import Framework from 'fancy-framework';
import './styles.scss';

...

Component-based architecture

Minimizing the number of requests

Prevent redundant requests

<script>
  INLINE_CACHE = {"users": [{ ... } ] };
</script>
  • Embed responses into the rendered page.
  • Can be reused on the client to do initial rendering.

shared code-base

HOw much code is shared?

Minimizing differences for easier maintainence

  • Same templating engine is used on server and client.
  • A "main" that runs in both contexts.
  • A shared router, not adding new routes in separate places

can-ssr Example

var ssr = require("can-ssr/middleware");
var app = require("express")();

app.use(ssr());

Middleware

var ssr = require("can-ssr");

var render = ssr();

render("/cart").then(function(result){
  console.log(result.html);
});

Core API

export function createState(request){
  // url could be: /, /profile, /messages, etc.
  const page = request.url.split("/").pop() || "home";

  return {
    page: page
  };
}

export function render(document, state){
  $("<title>").text("jQuery App").appendTo(document.head);

  let body = $(document.body);
  body.append(template());

  // Show the tab selected in the state
  body.find(`[aria-controls=${state.page}]`).tab("show");
}

asynchronous rendering

Synchronous Rendering

  • Forces all data to be present before rendering.
  • Cannot use component-based architecture.
  • Pushes application logic into another layer.
  • Makes writing reusable components harder.
// server.js
import render from "framework-dom";

app.get("/cart", function(req, res){

    fetchCart().then(function(data){
        res.send(
            render(data)
        );
    });

});
// cart.js
import Component from "fancy-framework";

class Cart extends Component {
    render() {
        let data = this.props.data;

        return <div> ... </div>
    }
}

Demo asynchronous react

INSTANT DEV WORKFLOW

hot module replacement

demo donejs live-reload

Take aways

  • Only include the JavaScript and CSS needed for the request.
  • Include any API responses in the response HTML. Reuse these in the client for initial rendering.
  • Use a router that works on both the client and server.
  • Component-based architecture is good, use it, with asynchronous rendering.

The End

BY Matthew Phillips

Server-Side Rendering Isn't Enough

By Matthew Phillips

Server-Side Rendering Isn't Enough

  • 379