Transcript
Page 1: Building Isomorphic Apps (JSConf.Asia 2014)

Building Isomorphic Apps @spikebrehm

Page 2: Building Isomorphic Apps (JSConf.Asia 2014)

Spike Brehm ____________________________

@spikebrehm

@AirbnbNerds

Page 3: Building Isomorphic Apps (JSConf.Asia 2014)

1. Why Isomorphic JavaScript 2. Isomorphic in the Wild 3. How to Isomorph

Page 4: Building Isomorphic Apps (JSConf.Asia 2014)

Why Isomorphic JavaScript

Page 5: Building Isomorphic Apps (JSConf.Asia 2014)

What is Isomorphic JavaScript?

Page 6: Building Isomorphic Apps (JSConf.Asia 2014)

JavaScript code that can be shared between environments.

Page 7: Building Isomorphic Apps (JSConf.Asia 2014)

i·so·mor·phicformsame

Page 8: Building Isomorphic Apps (JSConf.Asia 2014)

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

Page 9: Building Isomorphic Apps (JSConf.Asia 2014)

Persistence

Client JavaScriptDOM manipulation UX

View layerApplication

logic Routing

Backend Ruby

Python Java PHP

Ye Olde Web App

Page 10: Building Isomorphic Apps (JSConf.Asia 2014)

Backend Ruby

Python Java PHP

Persistence

Client JavaScriptDOM manipulation UX

View layerApplication

logic Routing

Fat-Client, circa 2011

Page 11: Building Isomorphic Apps (JSConf.Asia 2014)

Persistence

Client JavaScript

Shared JavaScript

View layerApplication

logic Routing

DOM manipulation UX

Backend Ruby

Python Java PHP

Isomorphic, circa 2013

Page 12: Building Isomorphic Apps (JSConf.Asia 2014)

Shared View Layer

Page 13: Building Isomorphic Apps (JSConf.Asia 2014)

Persistence

Client JavaScript

Shared JavaScript

View layerApplication

logic Routing

DOM manipulation UX

Backend Ruby

Python Java PHP

Isomorphic, circa 2013

Page 14: Building Isomorphic Apps (JSConf.Asia 2014)

View layer

Page 15: Building Isomorphic Apps (JSConf.Asia 2014)

Markup

Data presentation

URL formatting

Date, Currency formatting

I18n

Page 16: Building Isomorphic Apps (JSConf.Asia 2014)

Isomorphic Use Cases

1. “Single Page Apps” that can be fully rendered on the server.

2. Ambitious apps that share logic between client & server to accomplish novel things.

Page 17: Building Isomorphic Apps (JSConf.Asia 2014)

Why go to the trouble?

Page 18: Building Isomorphic Apps (JSConf.Asia 2014)

PerformanceInitial pageload speed.

SEO*Crawlable single-page apps.

FlexibilityRun code anywhere.

MaintainabilityReduce code duplication.

Page 19: Building Isomorphic Apps (JSConf.Asia 2014)

Performance

Page 20: Building Isomorphic Apps (JSConf.Asia 2014)

Client-rendered appDownload skeleton HTML

User sees content

Download JavaScript

Fetch data from API

Evaluate JavaScript

Exacerbated on mobile: high latency, low bandwidth

Page 21: Building Isomorphic Apps (JSConf.Asia 2014)

Server-rendered appDownload

full HTML

Download JavaScript

User sees content

Evaluate JavaScript

Page 22: Building Isomorphic Apps (JSConf.Asia 2014)

Isomorphic JavaScript in the Wild

Page 23: Building Isomorphic Apps (JSConf.Asia 2014)

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

Flickr

Page 24: Building Isomorphic Apps (JSConf.Asia 2014)

! Facebook’s React library in a Django app.

Instagram*

Page 25: Building Isomorphic Apps (JSConf.Asia 2014)

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

Airbnb Mobile Web

Page 26: Building Isomorphic Apps (JSConf.Asia 2014)

! Entire App runtime synced between client & server.

Asana

Page 27: Building Isomorphic Apps (JSConf.Asia 2014)

! Realtime app framework. Meteor

Page 28: Building Isomorphic Apps (JSConf.Asia 2014)

Isomorphic JavaScript is a spectrum

Page 29: Building Isomorphic Apps (JSConf.Asia 2014)

Entire view layer and app

logic shared

Small bits of view layer or logic shared

Page 30: Building Isomorphic Apps (JSConf.Asia 2014)

Many abstractions

Few abstractions

Page 31: Building Isomorphic Apps (JSConf.Asia 2014)

How to Isomorph

Page 32: Building Isomorphic Apps (JSConf.Asia 2014)

Isomorphic JavaScript can beenvironment agnostic

orshimmed per environment.

Page 33: Building Isomorphic Apps (JSConf.Asia 2014)

Environment agnostic

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

Page 34: Building Isomorphic Apps (JSConf.Asia 2014)

E X A M P L E

Handlebars.js

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

Page 35: Building Isomorphic Apps (JSConf.Asia 2014)

Shimmed per environment

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

window.location.pathnamevs req.path

Page 36: Building Isomorphic Apps (JSConf.Asia 2014)

E X A M P L E

Superagent

superagent! .get('/api/posts.json')! .end(function(res) {! console.log(res.status, res.body, res.headers);! });

Page 37: Building Isomorphic Apps (JSConf.Asia 2014)

Abstractions

Page 38: Building Isomorphic Apps (JSConf.Asia 2014)

A B S T R A C T I O N

Cookies

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

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

Page 39: Building Isomorphic Apps (JSConf.Asia 2014)

A B S T R A C T I O N

Redirects

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

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

Server res.redirect('/login');

Page 40: Building Isomorphic Apps (JSConf.Asia 2014)

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

H A C K T I M E

Page 41: Building Isomorphic Apps (JSConf.Asia 2014)

H A C K T I M E

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

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

or

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

Page 42: Building Isomorphic Apps (JSConf.Asia 2014)

H A C K T I M E

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 43: Building Isomorphic Apps (JSConf.Asia 2014)

That looks kinda hard…

Page 44: Building Isomorphic Apps (JSConf.Asia 2014)
Page 45: Building Isomorphic Apps (JSConf.Asia 2014)

NPM & Browserify* to the rescue

Page 46: Building Isomorphic Apps (JSConf.Asia 2014)

Browserify* Use CommonJS to require() modules in the browser.

Page 47: Building Isomorphic Apps (JSConf.Asia 2014)

Browserify* Package dependencies from node_modules into our bundle.

Page 48: Building Isomorphic Apps (JSConf.Asia 2014)

*or Webpack.

Webpack is like Browserify, but with more bells-and-whistles included by default.

Used by Instagram, Facebook, Yahoo!.

Page 49: Building Isomorphic Apps (JSConf.Asia 2014)

H A C K T I M E

Caveat: It’s Different on the Server

! setCookie('myCookie', 'the value', {res: res});!!!!

app.use(function(req, res, next) {! setCookie('myCookie', 'the value', {res: res});! ...!! next();!});

Page 50: Building Isomorphic Apps (JSConf.Asia 2014)

How do we make a shimmed-per-environment module?

Utilize package.json’s “browser” field.

Page 51: Building Isomorphic Apps (JSConf.Asia 2014)

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

Page 52: Building Isomorphic Apps (JSConf.Asia 2014)

Swap out the entire implementation.

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

Page 53: Building Isomorphic Apps (JSConf.Asia 2014)

Swap out specific files.

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

Page 54: Building Isomorphic Apps (JSConf.Asia 2014)

Swap out dependencies.

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

Page 55: Building Isomorphic Apps (JSConf.Asia 2014)

Let’s build `set-cookie`.

https://github.com/spikebrehm/set-cookie

Page 56: Building Isomorphic Apps (JSConf.Asia 2014)

Module structure

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

Page 57: Building Isomorphic Apps (JSConf.Asia 2014)

// ./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 58: Building Isomorphic Apps (JSConf.Asia 2014)

// ./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 59: Building Isomorphic Apps (JSConf.Asia 2014)

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

Page 60: Building Isomorphic Apps (JSConf.Asia 2014)

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

Page 61: Building Isomorphic Apps (JSConf.Asia 2014)

// ./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 62: Building Isomorphic Apps (JSConf.Asia 2014)

Projects of Note

Page 63: Building Isomorphic Apps (JSConf.Asia 2014)

React Reactive UI component library from Facebook. Designed from the ground up to support isomorphic rendering. http://facebook.github.io/react/

var UserProfile = React.createClass({ render: function() { return <div> <img src={this.props.user.thumbnailUrl} /> <h3>{this.props.user.name}</h3> </div>; } });   React.render(<UserProfile user={user} />, mountNode);

Page 64: Building Isomorphic Apps (JSConf.Asia 2014)

var UserProfile = React.createClass({ render: function() { return <div> <img src={this.props.user.thumbnailUrl} /> <h3>{this.props.user.name}</h3> </div>; } });   React.render(<UserProfile user={user} />, mountNode);

React Reactive UI component library from Facebook. Designed from the ground up to support isomorphic rendering. http://facebook.github.io/react/

Page 65: Building Isomorphic Apps (JSConf.Asia 2014)

var UserProfile = React.createClass({ render: function() { return <div> <img src={this.props.user.thumbnailUrl} /> <h3>{this.props.user.name}</h3> </div>; } });   var html = React.renderToString(<UserProfile user={user} />);

React Reactive UI component library from Facebook. Designed from the ground up to support isomorphic rendering. http://facebook.github.io/react/

Page 66: Building Isomorphic Apps (JSConf.Asia 2014)

Fluxible Yahoo’s isomorphic Flux implementation: Dispatchr, Fetchr, Routr. Provides a way to “dehydrate” server state and “rehydrate” on client. https://github.com/yahoo/flux-examples

Page 67: Building Isomorphic Apps (JSConf.Asia 2014)

Isobuild Meteor’s build system for isomorphic apps. Like Browserify & Webpack, uses static analysis to compute dependencies. Can target client, server, Android, or iOS. https://www.meteor.com/isobuild

if (Meteor.isClient) { // counter starts at 0 Session.setDefault("counter", 0);   Template.hello.events({ 'click button': function () { // increment the counter when button is clicked Session.set("counter", Session.get("counter") + 1); } }); }   if (Meteor.isServer) { Meteor.startup(function () { // code to run on server at startup }); }

Page 68: Building Isomorphic Apps (JSConf.Asia 2014)

Isobuild Meteor’s build system for isomorphic apps. Like Browserify & Webpack, uses static analysis to compute dependencies. Can target client, server, Android, or iOS. https://www.meteor.com/isobuild

if (Meteor.isClient) { // counter starts at 0 Session.setDefault("counter", 0);   Template.hello.events({ 'click button': function () { // increment the counter when button is clicked Session.set("counter", Session.get("counter") + 1); } }); }   if (Meteor.isServer) { Meteor.startup(function () { // code to run on server at startup }); }

Page 69: Building Isomorphic Apps (JSConf.Asia 2014)

isomorphic-tutorial Small sample isomorphic app, written from scratch using Handlebars/React, Director (routing), and Superagent (API requests). https://github.com/spikebrehm/isomorphic-tutorial

// app/routes.js var apiClient = require('./api_client');   module.exports = function(match) { match('/posts', function(callback) { apiClient.get('/posts.json', function(err, res) { if (err) return callback(err); var posts = res.body; callback(null, 'posts', {posts: posts}); }); }); };

Page 70: Building Isomorphic Apps (JSConf.Asia 2014)

isomorphic-tutorial Small sample isomorphic app, written from scratch using Handlebars/React, Director (routing), and Superagent (API requests). https://github.com/spikebrehm/isomorphic-tutorial

<h1>Posts</h1>   <ul> {{#each posts}} <li><a href="/posts/{{id}}">{{title}}</a></li> {{/each}} </ul>

Page 71: Building Isomorphic Apps (JSConf.Asia 2014)

Thanks! More resources available at

http://spike.technology !

@spikebrehm @AirbnbNerds


Top Related