Transcript
Page 1: JSConf US 2014: Building Isomorphic Apps

Building Isomorphic Apps

Spike Brehm @spikebrehm

Page 2: JSConf US 2014: Building Isomorphic Apps

@spikebrehm @AirbnbNerds

Spike Brehm

Page 3: JSConf US 2014: Building Isomorphic Apps

Isomorphic JavaScript.

Page 4: JSConf US 2014: Building Isomorphic Apps

WTF is Isomorphic JavaScript?

Page 5: JSConf US 2014: Building Isomorphic Apps

JavaScript code that can be shared between environments.

Page 6: JSConf US 2014: Building Isomorphic Apps

JavaScript code that can be shared between environments.

Page 7: JSConf US 2014: Building Isomorphic Apps

JavaScript code that can be shared between environments.

Page 8: JSConf US 2014: Building Isomorphic Apps

JavaScript code that can be shared between environments.

Page 9: JSConf US 2014: Building Isomorphic Apps

Backend Ruby

Python Java PHP

Node.js

Persistence

Client JavaScript

Shared JavaScript

DOM manipulation UX

View layer Application logic Routing

Page 10: JSConf US 2014: Building Isomorphic Apps

Etymology of “Isomorphic JavaScript”.

Page 11: JSConf US 2014: Building Isomorphic Apps

adjective corresponding or similar in form and relations.

i·so·mor·phic

Page 12: JSConf US 2014: Building Isomorphic Apps

i·so·mor·phicformsame

Page 13: JSConf US 2014: Building Isomorphic Apps

http://blog.nodejitsu.com/scaling-isomorphic-javascript-code/

Page 14: JSConf US 2014: Building Isomorphic Apps

“monomorphic” “heteromorphic” “homomorphic” “polymorphic”

You’re using it wrong!

Page 15: JSConf US 2014: Building Isomorphic Apps
Page 16: JSConf US 2014: Building Isomorphic Apps

Isomorphic JavaScript in the wild.

Page 17: JSConf US 2014: Building Isomorphic Apps

! Yahoo’s Modown libraries Flickr

Page 18: JSConf US 2014: Building Isomorphic Apps

! Yahoo’s Modown libraries Flickr

Page 19: JSConf US 2014: Building Isomorphic Apps

! Yahoo’s Modown libraries (successor to Mojito).

Flickr

Page 20: JSConf US 2014: Building Isomorphic Apps

! Facebook’s React library Instagram*

Page 21: JSConf US 2014: Building Isomorphic Apps

! Facebook’s React library Instagram*

Page 22: JSConf US 2014: Building Isomorphic Apps

! Facebook’s React library in a Django app.

Instagram*

Page 23: JSConf US 2014: Building Isomorphic Apps

! Airbnb’s Rendr library, built on Airbnb Mobile

Page 24: JSConf US 2014: Building Isomorphic Apps

! Airbnb’s Rendr library, built on Airbnb Mobile

Page 25: JSConf US 2014: Building Isomorphic Apps

! Airbnb’s Rendr library, built on Backbone and Express.

Airbnb Mobile

Page 26: JSConf US 2014: Building Isomorphic Apps

! Entire App runtime synced Asana

Page 27: JSConf US 2014: Building Isomorphic Apps

! Entire App runtime synced between client & server.

Asana

Page 28: JSConf US 2014: Building Isomorphic Apps

! Realtime app framework. Meteor

Page 29: JSConf US 2014: Building Isomorphic Apps

! Realtime app framework. Meteor

Page 30: JSConf US 2014: Building Isomorphic Apps

Wy go to the trouble?

Page 31: JSConf US 2014: Building Isomorphic Apps

Initial pageload speed.Performance

Crawlable single-page apps.SEO*

Reduce code duplication.MaintainabilityRun code anywhere.Flexibility

Page 32: JSConf US 2014: Building Isomorphic Apps

Isomorphic use cases.

Page 33: JSConf US 2014: Building Isomorphic Apps

• Templating • I18n • Date & currency formatting • Application logic • Routing • Model validation • API interaction • ...?

Isomorphic use cases.

Page 34: JSConf US 2014: Building Isomorphic Apps

Isomorphic JavaScript is a spectrum.

Page 35: JSConf US 2014: Building Isomorphic Apps

Entire view layer and app

logic shared

Small bits of view layer or logic shared

Page 36: JSConf US 2014: Building Isomorphic Apps

Many abstractions

Few abstractions

Page 37: JSConf US 2014: Building Isomorphic Apps

View layer shared

Entire app runtime synced between client

& server

Page 38: JSConf US 2014: Building Isomorphic Apps

Isomorphic JavaScript can be

or shimmed per environment .

environment-agnostic

Page 39: JSConf US 2014: Building Isomorphic Apps

Does not depend on browser-specific properties (window) or server-specific properties (process.env, req.cookies).

Environment-agnostic

Page 40: JSConf US 2014: Building Isomorphic Apps

Example: Handlebars.js

var template = ! '<ul>' \! '{{#each posts}}' \! ' <li>{{title}}</li>' \! '{{/each}}' \! '</ul>'!;! !var templateFn = Handlebars.compile(template)! , html = templateFn({posts: posts});

Page 41: JSConf US 2014: Building Isomorphic Apps

Provide shims for accessing environment-specific properties so module can expose a single API.

window.location.pathnamevs. req.path

Shimmed per environment

Page 42: JSConf US 2014: Building Isomorphic Apps

Example: Superagent

superagent! .get('/api/posts.json')! .end(function(res) {! if (res.status === 200) {! console.log("Posts:", res.body);! } else {! console.error("Error");! }! });

Page 43: JSConf US 2014: Building Isomorphic Apps

Abstractions.

Page 44: JSConf US 2014: Building Isomorphic Apps

Abstraction: User Agent

Client navigator.userAgent

Server req.get('user-agent')

Page 45: JSConf US 2014: Building Isomorphic Apps

Abstraction: Cookies

Client document.cookie =! 'myCookie=1; Domain=.example.org';

Server res.setHeader(! 'Set-Cookie: myCookie=1; ' +! 'Domain=.example.org'! );

Page 46: JSConf US 2014: Building Isomorphic Apps

Abstraction: Redirects

Clientdocument.location.href = '/login';!

!window.pushState({}, '', '/login');

Server res.redirect('/login');

Page 47: JSConf US 2014: Building Isomorphic Apps

How to isomorph.

Page 48: JSConf US 2014: Building Isomorphic Apps

Let’s write a module that abstracts the setting of cookies, providing the same API for client & server.

Page 49: JSConf US 2014: Building Isomorphic Apps

setCookie('myCookie', 'the value');

Page 50: JSConf US 2014: Building Isomorphic Apps

setCookie('myCookie', 'the value');

document.cookie = 'myCookie=the%20value';

or

res.setHeader('Set-Cookie: myCookie=the%20value;');

Page 51: JSConf US 2014: Building Isomorphic Apps

setCookie('myCookie', 'the value', {! path: '/',! domain: '.example.org',! expires: new Date(2014, 12, 31)!});

document.cookie =! 'myCookie=the%20value; Domain=.example.org; ' +! 'Path=/; Expires=Sat, 31 Jan 2015 05:00:00 GMT';

Page 52: JSConf US 2014: Building Isomorphic Apps

Eww, that looks hard.

Page 53: JSConf US 2014: Building Isomorphic Apps
Page 54: JSConf US 2014: Building Isomorphic Apps

NPM & Browserify to the rescue.

Page 55: JSConf US 2014: Building Isomorphic Apps

BrowserifyUse CommonJS to require() modules in the browser.

Page 56: JSConf US 2014: Building Isomorphic Apps

BrowserifyPackage dependencies from node_modules into our bundle.

Page 57: JSConf US 2014: Building Isomorphic Apps

How do we make a shimmed-per-environment module?Utilize package.json “browser” field.

Page 58: JSConf US 2014: Building Isomorphic Apps

{! "name": "set-cookie",! "dependencies": {...}!}!!!!

Page 59: JSConf US 2014: Building Isomorphic Apps

{! "name": "set-cookie",! "dependencies": {...},! "browser": "./lib/client.js"!}!!!

Swap out the entire implementation.

Page 60: JSConf US 2014: Building Isomorphic Apps

{! "name": "set-cookie",! "dependencies": {...},! "browser": {! "./lib/node.js": "./lib/client.js"! }!}!

Swap out specific files.

Page 61: JSConf US 2014: Building Isomorphic Apps

{! "name": "set-cookie",! "dependencies": {...},! "browser": {! "./lib/node.js": "./lib/client.js",! "cookie": "cookie-browser"! }!}

Swap out dependencies.

Page 62: JSConf US 2014: Building Isomorphic Apps

Let’s build `set-cookie`.https://github.com/spikebrehm/set-cookie

Page 63: JSConf US 2014: Building Isomorphic Apps

Module structure.!"## index.js!"## lib!$   %## setter!$   "## index.js!$   %## client.js!"## node_modules!$   %## cookie

Page 64: JSConf US 2014: Building Isomorphic Apps

// ./index.js!!var cookie = require('cookie');!var setter = require('./lib/setter');!!module.exports = function(name, value, options) {!  var cookieStr = cookie.serialize(name, value, options);!  setter(cookieStr, options);!};

Page 65: JSConf US 2014: Building Isomorphic Apps

// ./lib/setter/index.js!!module.exports = function setter(cookieStr, options) {!  var res = options && options.res;!!  if (!res)! throw new Error('Must specify `res` ' +! 'when setting cookie.’);!!  res.setHeader('Set-Cookie', cookieStr);!};

Page 66: JSConf US 2014: Building Isomorphic Apps

// ./lib/setter/client.js!!module.exports = function setter(cookieStr) {!  document.cookie = cookieStr;!};

Page 67: JSConf US 2014: Building Isomorphic Apps

// ./package.json!!{! "name": "set-cookie",! "dependencies": {! "cookie": "^0.1.2"! },! "browser": {! "./lib/setter/index.js": "./lib/setter/client.js"! }!}

Page 68: JSConf US 2014: Building Isomorphic Apps

// ./index.js!!var cookie = require('cookie');!var setter = require('./lib/setter');!!module.exports = function(name, value, options) {!  var cookieStr = cookie.serialize(name, value, options);!  setter(cookieStr, options);!};

Page 69: JSConf US 2014: Building Isomorphic Apps

How to isomorph in a nutshell.

Page 70: JSConf US 2014: Building Isomorphic Apps

@spikebrehm@AirbnbNerds

Thanks!More resources available at

http://spike.technology

Page 71: JSConf US 2014: Building Isomorphic Apps

@spikebrehm@AirbnbNerds

Thanks!More resources available at

http://spike.technology

We’re hiring!!!We’re hiring!!!We’re hiring!!!We’re hiring!!!We’re hiring!!!We’re hiring!!!