building isomorphic apps (jsconf.asia 2014)

Post on 02-Jul-2015

5.662 Views

Category:

Technology

0 Downloads

Preview:

Click to see full reader

DESCRIPTION

Over the past year or so, we’ve seen the emergence of a new way of building JavaScript web apps that share code between the web browser and the server, using Node.js — a technique that has come to be known as "isomorphic JavaScript.” There are a variety of use cases for isomorphic JavaScript; some apps render HTML on both the server and the client, some apps share just a few small bits of application logic, while others share the entire application runtime between client and server to provide advanced offline and realtime features. Why go isomorphic? The main benefits are performance, maintainability, reusability, and SEO. This talk shares examples of isomorphic JavaScript apps running in the wild, explore the exploding ecosystem of asset building tools, such as Browserify, Webpack, and Gulp, that allow developers to build their own isomorphic JavaScript apps with open-source libraries, demonstrate how to build an isomorphic JavaScript module from scratch, and explore how libraries like React and Flux can be used to build a single-page app that renders on the server.

TRANSCRIPT

Building Isomorphic Apps @spikebrehm

Spike Brehm ____________________________

@spikebrehm

@AirbnbNerds

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

Why Isomorphic JavaScript

What is Isomorphic JavaScript?

JavaScript code that can be shared between environments.

i·so·mor·phicformsame

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

Persistence

Client JavaScriptDOM manipulation UX

View layerApplication

logic Routing

Backend Ruby

Python Java PHP

Ye Olde Web App

Backend Ruby

Python Java PHP

Persistence

Client JavaScriptDOM manipulation UX

View layerApplication

logic Routing

Fat-Client, circa 2011

Persistence

Client JavaScript

Shared JavaScript

View layerApplication

logic Routing

DOM manipulation UX

Backend Ruby

Python Java PHP

Isomorphic, circa 2013

Shared View Layer

Persistence

Client JavaScript

Shared JavaScript

View layerApplication

logic Routing

DOM manipulation UX

Backend Ruby

Python Java PHP

Isomorphic, circa 2013

View layer

Markup

Data presentation

URL formatting

Date, Currency formatting

I18n

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.

Why go to the trouble?

PerformanceInitial pageload speed.

SEO*Crawlable single-page apps.

FlexibilityRun code anywhere.

MaintainabilityReduce code duplication.

Performance

Client-rendered appDownload skeleton HTML

User sees content

Download JavaScript

Fetch data from API

Evaluate JavaScript

Exacerbated on mobile: high latency, low bandwidth

Server-rendered appDownload

full HTML

Download JavaScript

User sees content

Evaluate JavaScript

Isomorphic JavaScript in the Wild

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

Flickr

! Facebook’s React library in a Django app.

Instagram*

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

Airbnb Mobile Web

! Entire App runtime synced between client & server.

Asana

! Realtime app framework. Meteor

Isomorphic JavaScript is a spectrum

Entire view layer and app

logic shared

Small bits of view layer or logic shared

Many abstractions

Few abstractions

How to Isomorph

Isomorphic JavaScript can beenvironment agnostic

orshimmed per environment.

Environment agnostic

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

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});

Shimmed per environment

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

window.location.pathnamevs req.path

E X A M P L E

Superagent

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

Abstractions

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'! );

A B S T R A C T I O N

Redirects

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

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

Server res.redirect('/login');

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

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;');

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';

That looks kinda hard…

NPM & Browserify* to the rescue

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

Browserify* Package dependencies from node_modules into our bundle.

*or Webpack.

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

Used by Instagram, Facebook, Yahoo!.

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();!});

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

Utilize package.json’s “browser” field.

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

Swap out the entire implementation.

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

Swap out specific files.

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

Swap out dependencies.

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

Let’s build `set-cookie`.

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

Module structure

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

// ./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);!};

// ./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);!};

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

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

// ./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);!};

Projects of Note

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);

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/

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/

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

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 }); }

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 }); }

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}); }); }); };

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>

Thanks! More resources available at

http://spike.technology !

@spikebrehm @AirbnbNerds

top related