82
Introducing Rendr: Run your Backbone.js apps on the client and server Spike Brehm @spikebrehm HTML5DevConf April 1, 2013

Introducing Rendr: Run your Backbone.js apps on the client and server

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: Introducing Rendr: Run your Backbone.js apps on the client and server

Introducing Rendr:Run your Backbone.js apps on the client and server

Spike Brehm@spikebrehm

HTML5DevConfApril 1, 2013

Page 2: Introducing Rendr: Run your Backbone.js apps on the client and server

2008

Page 3: Introducing Rendr: Run your Backbone.js apps on the client and server
Page 4: Introducing Rendr: Run your Backbone.js apps on the client and server

2013

Page 5: Introducing Rendr: Run your Backbone.js apps on the client and server
Page 6: Introducing Rendr: Run your Backbone.js apps on the client and server

Exciting times in the world of web apps.

<your framework here>

Page 7: Introducing Rendr: Run your Backbone.js apps on the client and server

Client-sideMVC

Page 8: Introducing Rendr: Run your Backbone.js apps on the client and server
Page 9: Introducing Rendr: Run your Backbone.js apps on the client and server

+

Page 10: Introducing Rendr: Run your Backbone.js apps on the client and server

Poor SEO; not crawlable

Performance hit to download and parse JS

Duplicated application logic

Context switching

Page 11: Introducing Rendr: Run your Backbone.js apps on the client and server

It’s still a PITA to build fast, maintainable rich-client apps.

Page 12: Introducing Rendr: Run your Backbone.js apps on the client and server

What if our JavaScript app could run on both sides?

We started thinking...

Page 13: Introducing Rendr: Run your Backbone.js apps on the client and server

Client +serverMVCaka “The Holy Grail”

Page 14: Introducing Rendr: Run your Backbone.js apps on the client and server

Provides SEO

Initial pageload is drastically faster

Consolidated application logic

Page 15: Introducing Rendr: Run your Backbone.js apps on the client and server

Has anyone already done this?

Page 16: Introducing Rendr: Run your Backbone.js apps on the client and server

Meteor: client/server, but no server-side rendering; owns data layer

Derby: client+server rendering, but owns data layer

Mojito: client+server rendering, but full stack, and... YUI.

Page 17: Introducing Rendr: Run your Backbone.js apps on the client and server

Okay... how hard can it be?

Page 18: Introducing Rendr: Run your Backbone.js apps on the client and server
Page 19: Introducing Rendr: Run your Backbone.js apps on the client and server

+

Page 20: Introducing Rendr: Run your Backbone.js apps on the client and server

+

Page 21: Introducing Rendr: Run your Backbone.js apps on the client and server

Rendr.

Page 22: Introducing Rendr: Run your Backbone.js apps on the client and server

What it is.

Page 23: Introducing Rendr: Run your Backbone.js apps on the client and server

JavaScript MVC on client & server

Backbone & Handlebars

BaseView, BaseModel, BaseCollection, BaseApp, ClientRouter, ServerRouter...

Express middleware

Minimal glue between client & server

What it is.

Page 24: Introducing Rendr: Run your Backbone.js apps on the client and server

What it ain’t.

Page 25: Introducing Rendr: Run your Backbone.js apps on the client and server

Batteries-included web framework

Finished

What it ain’t.

Page 26: Introducing Rendr: Run your Backbone.js apps on the client and server

Design goals:

• Write application logic agnostic to environment.

• Library, not a framework.

• Minimize if (server) {...} else {...}.

• Hide complexity in library.

• Talk to RESTful API.

• No server-side DOM.

• Simple Express middleware.

Page 27: Introducing Rendr: Run your Backbone.js apps on the client and server

Classes:

- BaseApp < Backbone.Model- BaseModel < Backbone.Model- BaseCollection < Backbone.Collection- BaseView < Backbone.View- AppView < BaseView

- ClientRouter < BaseRouter- ServerRouter < BaseRouter

- ModelStore < MemoryStore- CollectionStore < MemoryStore

- Fetcher

Page 28: Introducing Rendr: Run your Backbone.js apps on the client and server

|- client/|- shared/|- server/

Rendr directory structure

Sent to client}

Page 29: Introducing Rendr: Run your Backbone.js apps on the client and server

|- app/|- public/|- server/

App directory structure

Page 30: Introducing Rendr: Run your Backbone.js apps on the client and server

|- app/|--- collections/|--- controllers/|--- models/|--- templates/|--- views/|--- app.js|--- router.js|--- routes.js|- public/|- server/

App directory structure

Entire app dir gets sent to client}

Page 31: Introducing Rendr: Run your Backbone.js apps on the client and server

var User = require(‘app/models/user’);var BaseView = require(‘rendr/shared/base/view’);

CommonJS using Stitch

On the server:

On the client:var User = require(‘app/models/user’);var BaseView = require(‘rendr/shared/base/view’);

Page 32: Introducing Rendr: Run your Backbone.js apps on the client and server

module.exports = function(match) { match('', 'home#index'); match('users', 'users#index'); match('users/:id', 'users#show');};

app/routes.js

Page 33: Introducing Rendr: Run your Backbone.js apps on the client and server

app/routes.js

module.exports = function(match) { match('', 'home#index'); match('users', 'users#index'); match('users/:id', 'users#show');};

controller

Page 34: Introducing Rendr: Run your Backbone.js apps on the client and server

app/routes.js

module.exports = function(match) { match('', 'home#index'); match('users', 'users#index'); match('users/:id', 'users#show');};

action

Page 35: Introducing Rendr: Run your Backbone.js apps on the client and server

Render lifecycle, server.

Page 36: Introducing Rendr: Run your Backbone.js apps on the client and server

• On server startup, parse routes file and mount as Express routes

• GET /users/1337• Router matches "/users/:id" to "users#show" with

params = {"id": 1337}• Router finds controller:

require("app/controllers/users_controller")• Router executes show action with params = {"id": 1337}• The show action says to fetch User#1337 and use

UsersShowView view class• Router instantiates new UsersShowView with data• Router calls view.getHtml()• Hands HTML to Express, which decorates with layout

and serves response

Page 37: Introducing Rendr: Run your Backbone.js apps on the client and server

Render lifecycle, client.

Page 38: Introducing Rendr: Run your Backbone.js apps on the client and server

• On page load, Router parses routes file and mounts Backbone routes

• pushState /users/1337• Router matches "/users/:id" to "users#show" with

params = {"id": 1337}• Router finds controller:

require("app/controllers/users_controller")• Router executes show action with params = {"id": 1337}• The show action says to fetch User#1337 and use

UsersShowView view class• Router instantiates new UsersShowView with data• Router calls view.render()• Insert into DOM

Page 39: Introducing Rendr: Run your Backbone.js apps on the client and server

• On page load, Router parses routes file and mounts Backbone routes

• pushState /users/1337• Router matches "/users/:id" to "users#show" with

params = {"id": 1337}• Router finds controller:

require("app/controllers/users_controller")• Router executes show action with params = {"id": 1337}• The show action says to fetch User#1337 and use

UsersShowView view class• Router instantiates new UsersShowView with data• Router calls view.render()• Insert into DOM

Page 40: Introducing Rendr: Run your Backbone.js apps on the client and server

• On page load, Router parses routes file and mounts Backbone routes

• pushState /users/1337• Router matches "/users/:id" to "users#show" with

params = {"id": 1337}• Router finds controller:

require("app/controllers/users_controller")• Router executes show action with params = {"id": 1337}• The show action says to fetch User#1337 and use

UsersShowView view class• Router instantiates new UsersShowView with data• Router calls view.render()• Insert into DOM

Page 41: Introducing Rendr: Run your Backbone.js apps on the client and server

module.exports = { show: function(params, callback) { callback(null, 'users_show_view'); }};

app/controllers/users_controller.js

Page 42: Introducing Rendr: Run your Backbone.js apps on the client and server

app/controllers/users_controller.js

module.exports = { show: function(params, callback) { callback(null, 'users_show_view'); }};

Page 43: Introducing Rendr: Run your Backbone.js apps on the client and server

app/controllers/users_controller.js

module.exports = { show: function(params, callback) { callback(null, 'users_show_view'); }};

Page 44: Introducing Rendr: Run your Backbone.js apps on the client and server

app/controllers/users_controller.js

module.exports = { show: function(params, callback) { callback(null, 'users_show_view'); }};

Page 45: Introducing Rendr: Run your Backbone.js apps on the client and server

app/controllers/users_controller.js

Most simple case: no fetching of data.

module.exports = { show: function(params, callback) { callback(null, 'users_show_view'); }};

Page 46: Introducing Rendr: Run your Backbone.js apps on the client and server

app/controllers/users_controller.js

module.exports = { show: function(params, callback) { callback(null, 'users_show_view'); }};

But we want to fetch the user.

Page 47: Introducing Rendr: Run your Backbone.js apps on the client and server

app/controllers/users_controller.js

module.exports = { show: function(params, callback) { var spec = { model: {model: ‘User’, params: params} }; this.app.fetch(spec, function(err, results) { callback(err, 'users_show_view', results); }); }};

Page 48: Introducing Rendr: Run your Backbone.js apps on the client and server

app/controllers/users_controller.js

module.exports = { show: function(params, callback) { var spec = { model: {model: ‘User’, params: params} }; this.app.fetch(spec, function(err, results) { callback(err, 'users_show_view', results); }); }};

Page 49: Introducing Rendr: Run your Backbone.js apps on the client and server

app/controllers/users_controller.js

module.exports = { show: function(params, callback) { var spec = { model: {model: ‘User’, params: params} }; this.app.fetch(spec, function(err, results) { callback(err, 'users_show_view', results); }); }};

Page 50: Introducing Rendr: Run your Backbone.js apps on the client and server

app/controllers/users_controller.js

module.exports = { show: function(params, callback) { var spec = { model: {model: ‘User’, params: params} }; this.app.fetch(spec, function(err, results) { callback(err, 'users_show_view', results); }); }};

Page 51: Introducing Rendr: Run your Backbone.js apps on the client and server

var BaseView = require('rendr/shared/base/view');

module.exports = BaseView.extend({ className: 'users_show_view'});module.exports.id = 'users_show_view';

app/views/users_show_view.js

Page 52: Introducing Rendr: Run your Backbone.js apps on the client and server

var BaseView = require('rendr/shared/base/view');

module.exports = BaseView.extend({ className: 'users_show_view'});module.exports.id = 'users_show_view';

app/views/users_show_view.js

Page 53: Introducing Rendr: Run your Backbone.js apps on the client and server

var BaseView = require('rendr/shared/base/view');

module.exports = BaseView.extend({ className: 'users_show_view'});module.exports.id = 'users_show_view';

app/views/users_show_view.js

Page 54: Introducing Rendr: Run your Backbone.js apps on the client and server

var BaseView = require('rendr/shared/base/view');

module.exports = BaseView.extend({ className: 'users_show_view'});module.exports.id = 'users_show_view';

app/views/users_show_view.js

Page 55: Introducing Rendr: Run your Backbone.js apps on the client and server

var BaseView = require('rendr/shared/base/view');

module.exports = BaseView.extend({ className: 'users_show_view',

events: { 'click p': 'handleClick' },

handleClick: function() {...}});module.exports.id = 'users_show_view';

app/views/users_show_view.js

Page 56: Introducing Rendr: Run your Backbone.js apps on the client and server

<h1>User: {{name}}</h1>

<p>From {{city}}.</p>

app/templates/users_show_view.hbs

Page 57: Introducing Rendr: Run your Backbone.js apps on the client and server

Rendered HTML

<div class="users_show_view" data-view="users_show_view" data-model_name="user" data-model_id="1337">

<h1>User: Spike</h1>

<p>From San Francisco.</p></div>

Page 58: Introducing Rendr: Run your Backbone.js apps on the client and server

Rendered HTML

<div class="users_show_view" data-view="users_show_view" data-model_name="user" data-model_id="1337">

<h1>User: Spike</h1>

<p>From San Francisco.</p></div>

Page 59: Introducing Rendr: Run your Backbone.js apps on the client and server

Rendered HTML

<div class="users_show_view" data-view="users_show_view" data-model_name="user" data-model_id="1337">

<h1>User: Spike</h1>

<p>From San Francisco.</p></div>

Page 60: Introducing Rendr: Run your Backbone.js apps on the client and server

Rendered HTML

<div class="users_show_view" data-view="users_show_view" data-model_name="user" data-model_id="1337">

<h1>User: Spike</h1>

<p>From San Francisco.</p></div>

Page 61: Introducing Rendr: Run your Backbone.js apps on the client and server

Where’d the data come from?

Where’s the render method?

How do I customize what gets passed to the template?

Sensible defaults.

Page 62: Introducing Rendr: Run your Backbone.js apps on the client and server

var BaseView = require('rendr/shared/base/view');

module.exports = BaseView.extend({ className: 'users_show_view',

getTemplateData: function() {

}});

app/views/users_show_view.js

Page 63: Introducing Rendr: Run your Backbone.js apps on the client and server

var BaseView = require('rendr/shared/base/view');

module.exports = BaseView.extend({ className: 'users_show_view',

getTemplateData: function() { var data = BaseView.prototype.getTemplateData \ .call(this); }});

app/views/users_show_view.js

Page 64: Introducing Rendr: Run your Backbone.js apps on the client and server

var BaseView = require('rendr/shared/base/view');

module.exports = BaseView.extend({ className: 'users_show_view',

getTemplateData: function() { var data = BaseView.prototype.getTemplateData \ .call(this); // `data` is equivalent to this.model.toJSON()

}});

app/views/users_show_view.js

Page 65: Introducing Rendr: Run your Backbone.js apps on the client and server

var BaseView = require('rendr/shared/base/view');

module.exports = BaseView.extend({ className: 'users_show_view',

getTemplateData: function() { var data = BaseView.prototype.getTemplateData \ .call(this); // `data` is equivalent to this.model.toJSON() return _.extend(data, { nameUppercase: data.name.toUpperCase() }); }});

app/views/users_show_view.js

Page 66: Introducing Rendr: Run your Backbone.js apps on the client and server

<h1>User: {{nameUppercase}}</h1>

<p>From {{city}}.</p>

app/templates/users_show_view.hbs

Page 67: Introducing Rendr: Run your Backbone.js apps on the client and server

<div class="users_show_view" data-...> <h1>User: SPIKE</h1>

<p>From San Francisco.</p></div>

Rendered HTML

Page 68: Introducing Rendr: Run your Backbone.js apps on the client and server

<!doctype html><html lang="en"><head>...</head>

<body><div id="content"> <div class="users_show_view" data-view="users_show_view" data-model_name="user" data-model_id="1337">

<h1>User: SPIKE</h1>

<p>From San Francisco.</p> </div></div>

<script>(function() {var App = window.App = new (require('app/app'));App.bootstrapData({ "model":{"summary":{"model":"user","id":1337}, "data":{"name":"Spike","city":"San Francisco", ...}});App.start();})();</script></body></html>

Rendered HTML with layout

Page 69: Introducing Rendr: Run your Backbone.js apps on the client and server

<!doctype html><html lang="en"><head>...</head>

<body><div id="content"> <div class="users_show_view" data-view="users_show_view" data-model_name="user" data-model_id="1337">

<h1>User: SPIKE</h1>

<p>From San Francisco.</p> </div></div>

<script>(function() {var App = window.App = new (require('app/app'));App.bootstrapData({ "model":{"summary":{"model":"user","id":"wycats"}, "data":{"name":"Spike","city":"San Francisco", ...}});App.start();})();</script></body></html>

Rendered HTML with layout

Page 70: Introducing Rendr: Run your Backbone.js apps on the client and server

View hydration.

Page 71: Introducing Rendr: Run your Backbone.js apps on the client and server

1. Find DOM els with data-view attribute.

var viewEls = $("[data-view]");

Page 72: Introducing Rendr: Run your Backbone.js apps on the client and server

2. Determine model or collection based on data-model_name, data-model_id, etc.

var modelName = viewEl.data(‘model_name’), modelId = viewEl.data(‘model_id’);

console.log(modelName, modelId); => “user” 1337

Page 73: Introducing Rendr: Run your Backbone.js apps on the client and server

3. Fetch model/collection data from ModelStore/CollectionStore.

var model = modelStore.get(modelName, modelId);

Page 74: Introducing Rendr: Run your Backbone.js apps on the client and server

4. Find view class.

var viewName, ViewClass;viewName = viewEl.data(‘view’);ViewClass = require('app/views/' + viewName);

Page 75: Introducing Rendr: Run your Backbone.js apps on the client and server

5. Instantiate view instance with model.

var view = new ViewClass({ model: model, ...});

Page 76: Introducing Rendr: Run your Backbone.js apps on the client and server

6. Attach to DOM el.

view.setElement(viewEl);

Page 77: Introducing Rendr: Run your Backbone.js apps on the client and server

7. Delegate events.

view.delegateEvents();

Page 78: Introducing Rendr: Run your Backbone.js apps on the client and server

8. Profit!

alert(“That wasn’t so hard, right?”)

Page 79: Introducing Rendr: Run your Backbone.js apps on the client and server

Rendr is available starting today.

github.com/airbnb/rendr

Page 80: Introducing Rendr: Run your Backbone.js apps on the client and server

TODO

• Share routing logic between client & server.

• Lazy load views, templates, etc as needed.

• Support other templating languages.

• Break down into smaller modules.

• Rewrite in vanilla JavaScript.

• much more...

Page 81: Introducing Rendr: Run your Backbone.js apps on the client and server

Hackers wanted.

Page 82: Introducing Rendr: Run your Backbone.js apps on the client and server

Thanks!

@spikebrehm@rendrjs

@AirbnbNerds