web developer @ Evozon Systems

Andrei Cacio

Google <3 SPAs

What are single page applications?

When did we start talking about them?

- till 2004 there was no such thing as a SPA

 - "traditional" web applications

So what happened after 2004?

XMLHttpRequest

hapened

SEO before SPAs

- server-side template engines

- server-side render

- server-side routes

- mostly non dynamic content 

Web applications

Crawlers and bots

- Google, Bing, Yahoo etc.

- would request the HTML

- ES5 was the new standard

- The number of JavaScript frameworks started to grow: ExtJS, Angular, Backbone etc.

- Template engines on the browser

- Client-side routing

- Nodejs was released (btw)

- etc.

2009

- client-side template engines

- client-side render

- client-side routes

- mostly dynamic content 

Single page applications

SEO after SPAs

Crawlers and bots

- Google, Bing, Yahoo etc.

- would request the HTML

- client-side template engines

- client-side render

- client-side routes

- mostly dynamic content 

Single page applications

* Google AJAX crawl scheme

2011

Google bot (and other crawlers)

vs

Browser

PhantomJS

a headless WebKit scriptable

# get.js
const system = require('system');
const webPage = require('webpage');
const page = webPage.create();
const address = system.args[1]

page.open(address, function (status) {
  const content = page.content;
  console.log('Content: ' + content);
  phantom.exit();
});
$ phantomjs get.js http://google.com

Let's do some SEO with

PhantomJS

Demo

since the middle of 2014

... ish

Routes

#!

  • The hash URLs
  • Google AJAX crawl scheme
<meta name="fragment" content="!">

URL: /#!/page
Google bot: /_escaped_fragment=/page

It's deprecated!

 

... since late 2015

Routes

/

  • Pretty URLs
  • Google treats them as a normal routes
  • HTML5 pushState

Route mirroring

var app = angular.module('spaapp', []);
  
app.config(['$routeProvider', '$locationProvider'
  function($routeProvider, $locationProvider) {
    $routeProvider.
      when('/products', {
        templateUrl: 'templates/products.html',
        controller: 'ProductsController'
      }).
      when('/about', {
        templateUrl: 'templates/about.html',
        controller: 'AboutController'
      }).
      otherwise({
        redirectTo: '/'
      });
    $locationProvider.html5Mode(true);
  }]);

AngularJS routes

const express = require('express');
const app = express();

app.get('/', (reg, res) => {
   res.render('index', { data: {} });
});

app.get('/about', (reg, res) => {
   res.render('about', { data: {} });
});

app.get('/products', (reg, res) => {
   res.render('products', { data: {} });
});

Express routes

this should apply regardless of what framework is used on both client or server

"Precomposing " a SPA

  • Inline the content into a JSON object into your template
  • Avoid unnecessary AJAX calls
  • Lift the heavy weight off as much as possible
<!DOCTYPE html>
<html>
    <head></head>
    <body>
        <script>
            window.initialData = { ... };
        </script>
        <template id="my-template"> ... </template>
        <script src="app.min.js"></script>
    </body>
</html>

2015 - 2016

- can we do it better?

- could we avoid tools like PhantomJS

- and still have good SEO on other major crawlers?

We can go the isomorphic way

  • libraries that run on the client and on the server
  • leverage server-side rendering

How can we achieve this?

... among others

Write once, use everywhere

Super simple example

import React from 'react';
import ReactDOM from 'react-dom';
import Griddle from 'griddle-react';
import {fakeData} from '../fixtures/grid';

export const GriddleDefault = React.createClass({
    render() {
        return <Griddle results={fakeData} resultsPerPage={20}></Griddle>;
    }
});

GridComponent.js

import React from 'react';
import ReactDOM from 'react-dom';

import {Grid} from './components/grid';

ReactDOM.render(<Grid />, document.getElementById('grid'));

index.js

doctype html
html
  head
    title= title
  body
    h1= title
    div(id='grid')
        Loading ...
    script(src='/js/bundle.js')

index.jade

const express = require('express');
const React = require('react');
const renderToString = require('react-dom/server').renderToString;
const Grid = require('../client/app/components/grid');
const app = express();

app.get('/', (req, res) => {
    res.render('index', {
        react: renderToString(React.createElement(Grid))
    });
});

server.js

Thank you!

All resources and slides can be found here:

 andreicacio.com/google-hearts-spas/

Questions?

Google <3 SPAs

By Andrei Cacio

Google <3 SPAs

  • 2,590