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.
16. 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.
20. Client-rendered app
Download
skeleton
HTML
User sees
content
Download
JavaScript
Fetch data
from API
Evaluate
JavaScript
Exacerbated on mobile: high
latency, low bandwidth
33. Environment
agnostic
Does not depend on browser-specific properties
(window) or server-specific properties
(process.env, req.cookies).
34. 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});
35. Shimmed per
environment
Provide shims for accessing environment-specific
properties so module can expose a single API.
window.location.pathname
vs
req.path
36. E X A M P L E
Superagent
superagent!
.get('/api/posts.json')!
.end(function(res) {!
console.log(res.status, res.body, res.headers);!
});
38. A B S T R ACT I O N
Cookies
Client document.cookie =!
'myCookie=1; Domain=.example.org';
Server
res.setHeader(!
'Set-Cookie: myCookie=1; ' +!
'Domain=.example.org'!
);
39. A B S T R ACT I O N
Redirects
Client
document.location.href = '/login';!
!
window.pushState({}, '', '/login');
Server res.redirect('/login');
40. H AC K T I M E
Let’s write a module that abstracts the setting of
cookies, providing the same API for client & server.
41. H AC K T I M E
setCookie('myCookie', 'the value');
document.cookie = 'myCookie=the%20value';
or
res.setHeader('Set-Cookie: myCookie=the%20value;');
42. H AC 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';
48. *or Webpack.
Webpack is like Browserify, but with more bells-and-
whistles included by default.
Used by Instagram, Facebook, Yahoo!.
49. H AC K T I M E
Caveat: It’s Different on the Server
app.use(function(req, res, next) {!
...!
! next();!
});
!
setCookie('myCookie', 'the value', {res: res});!
!
!
!
50. How do we make a shimmed-per-environment module?
Utilize package.json’s “browser” field.
63. 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);
64. 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);
65. 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} />);
66. 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
67. 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
});
}
68. 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
});
}
69. 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});
});
});
};
70. 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>