SlideShare una empresa de Scribd logo
1 de 82
Descargar para leer sin conexión
Introducing Rendr:
Run your Backbone.js apps
on the client and server

Spike Brehm
@spikebrehm

HTML5DevConf
April 1, 2013
2008
2013
Exciting times in the
world of web apps.


<your framework here>
Client-side
MVC
+
Poor SEO; not crawlable
Performance hit to download
and parse JS
Duplicated application logic
Context switching
It’s still a PITA to build
fast, maintainable
rich-client apps.
We started thinking...

What if our JavaScript
app could run on both
sides?
Client +
server
MVC
aka “The Holy Grail”
Provides SEO
Initial pageload is drastically
faster
Consolidated application logic
Has anyone already
done this?
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.
Okay... how hard can
it be?
+
+
Rendr.
What it is.
What it is.
JavaScript MVC on client & server
Backbone & Handlebars
BaseView, BaseModel,
BaseCollection, BaseApp,
ClientRouter, ServerRouter...
Express middleware
Minimal glue between client & server
What it ain’t.
What it ain’t.

Batteries-included web framework
Finished
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.
Classes:
- BaseApp < Backbone.Model
- BaseModel < Backbone.Model
- BaseCollection < Backbone.Collection
- BaseView < Backbone.View
- AppView < BaseView
- ClientRouter < BaseRouter
- ServerRouter < BaseRouter
- ModelStore < MemoryStore
- CollectionStore < MemoryStore
- Fetcher
Rendr directory structure
|- client/
|- shared/   } Sent to client
|- server/
App directory structure
|- app/
|- public/
|- server/
App directory structure




                    }
|- app/
|--- collections/
|--- controllers/
|--- models/            Entire app dir
|--- templates/         gets sent to
|--- views/
|--- app.js
                        client
|--- router.js
|--- routes.js
|- public/
|- server/
CommonJS using Stitch

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


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


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


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


                     controller
app/routes.js


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


                        action
Render lifecycle,
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
Render lifecycle,
client.
•   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
•   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
•   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
app/controllers/users_controller.js

module.exports = {
   show: function(params, callback) {
     callback(null, 'users_show_view');
   }
};
app/controllers/users_controller.js

module.exports = {
   show: function(params, callback) {
     callback(null, 'users_show_view');
   }
};
app/controllers/users_controller.js

module.exports = {
   show: function(params, callback) {
     callback(null, 'users_show_view');
   }
};
app/controllers/users_controller.js

module.exports = {
   show: function(params, callback) {
     callback(null, 'users_show_view');
   }
};
app/controllers/users_controller.js

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




Most simple case: no fetching of
data.
app/controllers/users_controller.js

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




But we want to fetch the user.
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);
     });
   }
};
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);
     });
   }
};
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);
     });
   }
};
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);
     });
   }
};
app/views/users_show_view.js

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

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

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

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

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/templates/users_show_view.hbs



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

<p>From {{city}}.</p>
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>
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>
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>
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>
Where’d the data come from?
Where’s the render method?
How do I customize what gets
passed to the template?
Sensible defaults.
app/views/users_show_view.js
var BaseView = require('rendr/shared/base/view');

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

  getTemplateData: function() {

  }
});
app/views/users_show_view.js
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
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
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/templates/users_show_view.hbs



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

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


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

  <p>From San Francisco.</p>
</div>
Rendered HTML with layout
<!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
<!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>
View hydration.
1. Find DOM els with data-view attribute.
var viewEls = $("[data-view]");
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
3. Fetch model/collection data from
ModelStore/CollectionStore.
var model = modelStore.get(modelName,
              modelId);
4. Find view class.
var viewName, ViewClass;
viewName = viewEl.data(‘view’);
ViewClass = require('app/views/' + viewName);
5. Instantiate view instance with model.
var view = new ViewClass({
  model: model,
  ...
});
6. Attach to DOM el.
view.setElement(viewEl);
7. Delegate events.
view.delegateEvents();
8. Profit!
alert(“That wasn’t so hard, right?”)
Rendr is available
starting today.
github.com/airbnb/rendr
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...
Hackers
wanted.
Thanks!
@spikebrehm
  @rendrjs
@AirbnbNerds

Más contenido relacionado

La actualidad más candente

Angular를 활용한 웹 프론트단 개발과 2.0에서 달라진점
Angular를 활용한 웹 프론트단 개발과 2.0에서 달라진점Angular를 활용한 웹 프론트단 개발과 2.0에서 달라진점
Angular를 활용한 웹 프론트단 개발과 2.0에서 달라진점
Jeado Ko
 
No Coding Necessary: Building Confluence User Macros Cheat Sheet - Atlassian ...
No Coding Necessary: Building Confluence User Macros Cheat Sheet - Atlassian ...No Coding Necessary: Building Confluence User Macros Cheat Sheet - Atlassian ...
No Coding Necessary: Building Confluence User Macros Cheat Sheet - Atlassian ...
Atlassian
 

La actualidad más candente (20)

Vue, vue router, vuex
Vue, vue router, vuexVue, vue router, vuex
Vue, vue router, vuex
 
Angular js
Angular jsAngular js
Angular js
 
Vuex
VuexVuex
Vuex
 
Angular를 활용한 웹 프론트단 개발과 2.0에서 달라진점
Angular를 활용한 웹 프론트단 개발과 2.0에서 달라진점Angular를 활용한 웹 프론트단 개발과 2.0에서 달라진점
Angular를 활용한 웹 프론트단 개발과 2.0에서 달라진점
 
How to Build SPA with Vue Router 2.0
How to Build SPA with Vue Router 2.0How to Build SPA with Vue Router 2.0
How to Build SPA with Vue Router 2.0
 
Modern Web Application Development Workflow - EclipseCon Europe 2014
Modern Web Application Development Workflow - EclipseCon Europe 2014Modern Web Application Development Workflow - EclipseCon Europe 2014
Modern Web Application Development Workflow - EclipseCon Europe 2014
 
No Coding Necessary: Building Confluence User Macros Cheat Sheet - Atlassian ...
No Coding Necessary: Building Confluence User Macros Cheat Sheet - Atlassian ...No Coding Necessary: Building Confluence User Macros Cheat Sheet - Atlassian ...
No Coding Necessary: Building Confluence User Macros Cheat Sheet - Atlassian ...
 
Managing JavaScript Dependencies With RequireJS
Managing JavaScript Dependencies With RequireJSManaging JavaScript Dependencies With RequireJS
Managing JavaScript Dependencies With RequireJS
 
No Coding Necessary: Building User Macros and Dynamic Reports Inside Confluen...
No Coding Necessary: Building User Macros and Dynamic Reports Inside Confluen...No Coding Necessary: Building User Macros and Dynamic Reports Inside Confluen...
No Coding Necessary: Building User Macros and Dynamic Reports Inside Confluen...
 
Refactoring Large Web Applications with Backbone.js
Refactoring Large Web Applications with Backbone.jsRefactoring Large Web Applications with Backbone.js
Refactoring Large Web Applications with Backbone.js
 
An Introduction To Testing In AngularJS Applications
An Introduction To Testing In AngularJS Applications An Introduction To Testing In AngularJS Applications
An Introduction To Testing In AngularJS Applications
 
Introduction to Angularjs
Introduction to AngularjsIntroduction to Angularjs
Introduction to Angularjs
 
Planbox Backbone MVC
Planbox Backbone MVCPlanbox Backbone MVC
Planbox Backbone MVC
 
Javascript Frameworks for Well Architected, Immersive Web Apps
Javascript Frameworks for Well Architected, Immersive Web AppsJavascript Frameworks for Well Architected, Immersive Web Apps
Javascript Frameworks for Well Architected, Immersive Web Apps
 
Workshop 13: AngularJS Parte II
Workshop 13: AngularJS Parte IIWorkshop 13: AngularJS Parte II
Workshop 13: AngularJS Parte II
 
Getting Started with Angular JS
Getting Started with Angular JSGetting Started with Angular JS
Getting Started with Angular JS
 
AngularJS 101 - Everything you need to know to get started
AngularJS 101 - Everything you need to know to get startedAngularJS 101 - Everything you need to know to get started
AngularJS 101 - Everything you need to know to get started
 
MVC on the server and on the client
MVC on the server and on the clientMVC on the server and on the client
MVC on the server and on the client
 
Spring MVC
Spring MVCSpring MVC
Spring MVC
 
Vue business first
Vue business firstVue business first
Vue business first
 

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

Android app development basics
Android app development basicsAndroid app development basics
Android app development basics
Anton Narusberg
 
Dundee University HackU 2013 - Mojito
Dundee University HackU 2013 - MojitoDundee University HackU 2013 - Mojito
Dundee University HackU 2013 - Mojito
smartads
 

Similar a Introducing Rendr: Run your Backbone.js apps on the client and server (20)

Angular server side rendering - Strategies & Technics
Angular server side rendering - Strategies & Technics Angular server side rendering - Strategies & Technics
Angular server side rendering - Strategies & Technics
 
Building a dashboard using AngularJS
Building a dashboard using AngularJSBuilding a dashboard using AngularJS
Building a dashboard using AngularJS
 
Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...
Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...
Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...
 
Reactive application using meteor
Reactive application using meteorReactive application using meteor
Reactive application using meteor
 
Aurelia Meetup Paris
Aurelia Meetup ParisAurelia Meetup Paris
Aurelia Meetup Paris
 
Mini-Training: AngularJS
Mini-Training: AngularJSMini-Training: AngularJS
Mini-Training: AngularJS
 
AngularJS training - Day 1 - Basics: Why, What and basic features of AngularJS
AngularJS training - Day 1 - Basics: Why, What and basic features of AngularJSAngularJS training - Day 1 - Basics: Why, What and basic features of AngularJS
AngularJS training - Day 1 - Basics: Why, What and basic features of AngularJS
 
A gently introduction to AngularJS
A gently introduction to AngularJSA gently introduction to AngularJS
A gently introduction to AngularJS
 
AngularJS in 60ish Minutes - Dan Wahlin | FalafelCON 2014
AngularJS in 60ish Minutes - Dan Wahlin | FalafelCON 2014AngularJS in 60ish Minutes - Dan Wahlin | FalafelCON 2014
AngularJS in 60ish Minutes - Dan Wahlin | FalafelCON 2014
 
Optimizing Angular Performance in Enterprise Single Page Apps
Optimizing Angular Performance in Enterprise Single Page AppsOptimizing Angular Performance in Enterprise Single Page Apps
Optimizing Angular Performance in Enterprise Single Page Apps
 
Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications  Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications
 
AngularJS
AngularJSAngularJS
AngularJS
 
Angular js routing options
Angular js routing optionsAngular js routing options
Angular js routing options
 
Android app development basics
Android app development basicsAndroid app development basics
Android app development basics
 
Angular Workshop_Sarajevo2
Angular Workshop_Sarajevo2Angular Workshop_Sarajevo2
Angular Workshop_Sarajevo2
 
AngularJs-training
AngularJs-trainingAngularJs-training
AngularJs-training
 
Dundee University HackU 2013 - Mojito
Dundee University HackU 2013 - MojitoDundee University HackU 2013 - Mojito
Dundee University HackU 2013 - Mojito
 
Building an angular application -1 ( API: Golang, Database: Postgres) v1.0
Building an angular application -1 ( API: Golang, Database: Postgres) v1.0Building an angular application -1 ( API: Golang, Database: Postgres) v1.0
Building an angular application -1 ( API: Golang, Database: Postgres) v1.0
 
Asp.Net Mvc
Asp.Net MvcAsp.Net Mvc
Asp.Net Mvc
 
Building Web Apps with Express
Building Web Apps with ExpressBuilding Web Apps with Express
Building Web Apps with Express
 

Más de Spike Brehm

Integrating Browserify with Sprockets
Integrating Browserify with SprocketsIntegrating Browserify with Sprockets
Integrating Browserify with Sprockets
Spike Brehm
 
In Pursuit of the Holy Grail: Building Isomorphic JavaScript Apps
In Pursuit of the Holy Grail: Building Isomorphic JavaScript AppsIn Pursuit of the Holy Grail: Building Isomorphic JavaScript Apps
In Pursuit of the Holy Grail: Building Isomorphic JavaScript Apps
Spike Brehm
 
General Assembly Workshop: Advanced JavaScript
General Assembly Workshop: Advanced JavaScriptGeneral Assembly Workshop: Advanced JavaScript
General Assembly Workshop: Advanced JavaScript
Spike Brehm
 
Isomorphic JavaScript: #DevBeat Master Class
Isomorphic JavaScript: #DevBeat Master ClassIsomorphic JavaScript: #DevBeat Master Class
Isomorphic JavaScript: #DevBeat Master Class
Spike Brehm
 
Building a Single-Page App: Backbone, Node.js, and Beyond
Building a Single-Page App: Backbone, Node.js, and BeyondBuilding a Single-Page App: Backbone, Node.js, and Beyond
Building a Single-Page App: Backbone, Node.js, and Beyond
Spike Brehm
 
Extending Apostrophe to build a variable-based CMS for rendering PDF brochures
Extending Apostrophe to build a variable-based CMS for rendering PDF brochuresExtending Apostrophe to build a variable-based CMS for rendering PDF brochures
Extending Apostrophe to build a variable-based CMS for rendering PDF brochures
Spike Brehm
 

Más de Spike Brehm (11)

Managing Through Chaos (w/ presenter notes)
Managing Through Chaos (w/ presenter notes)Managing Through Chaos (w/ presenter notes)
Managing Through Chaos (w/ presenter notes)
 
Managing Through Chaos
Managing Through ChaosManaging Through Chaos
Managing Through Chaos
 
The Evolution of Airbnb's Frontend
The Evolution of Airbnb's FrontendThe Evolution of Airbnb's Frontend
The Evolution of Airbnb's Frontend
 
Integrating Browserify with Sprockets
Integrating Browserify with SprocketsIntegrating Browserify with Sprockets
Integrating Browserify with Sprockets
 
Building Isomorphic Apps (JSConf.Asia 2014)
Building Isomorphic Apps (JSConf.Asia 2014)Building Isomorphic Apps (JSConf.Asia 2014)
Building Isomorphic Apps (JSConf.Asia 2014)
 
JSConf US 2014: Building Isomorphic Apps
JSConf US 2014: Building Isomorphic AppsJSConf US 2014: Building Isomorphic Apps
JSConf US 2014: Building Isomorphic Apps
 
In Pursuit of the Holy Grail: Building Isomorphic JavaScript Apps
In Pursuit of the Holy Grail: Building Isomorphic JavaScript AppsIn Pursuit of the Holy Grail: Building Isomorphic JavaScript Apps
In Pursuit of the Holy Grail: Building Isomorphic JavaScript Apps
 
General Assembly Workshop: Advanced JavaScript
General Assembly Workshop: Advanced JavaScriptGeneral Assembly Workshop: Advanced JavaScript
General Assembly Workshop: Advanced JavaScript
 
Isomorphic JavaScript: #DevBeat Master Class
Isomorphic JavaScript: #DevBeat Master ClassIsomorphic JavaScript: #DevBeat Master Class
Isomorphic JavaScript: #DevBeat Master Class
 
Building a Single-Page App: Backbone, Node.js, and Beyond
Building a Single-Page App: Backbone, Node.js, and BeyondBuilding a Single-Page App: Backbone, Node.js, and Beyond
Building a Single-Page App: Backbone, Node.js, and Beyond
 
Extending Apostrophe to build a variable-based CMS for rendering PDF brochures
Extending Apostrophe to build a variable-based CMS for rendering PDF brochuresExtending Apostrophe to build a variable-based CMS for rendering PDF brochures
Extending Apostrophe to build a variable-based CMS for rendering PDF brochures
 

Último

+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
?#DUbAI#??##{{(☎️+971_581248768%)**%*]'#abortion pills for sale in dubai@
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
Joaquim Jorge
 

Último (20)

Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
HTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesHTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation Strategies
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdf
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 

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

  • 1. Introducing Rendr: Run your Backbone.js apps on the client and server Spike Brehm @spikebrehm HTML5DevConf April 1, 2013
  • 3.
  • 5.
  • 6. Exciting times in the world of web apps. <your framework here>
  • 8.
  • 9. +
  • 10. Poor SEO; not crawlable Performance hit to download and parse JS Duplicated application logic Context switching
  • 11. It’s still a PITA to build fast, maintainable rich-client apps.
  • 12. We started thinking... What if our JavaScript app could run on both sides?
  • 14. Provides SEO Initial pageload is drastically faster Consolidated application logic
  • 16. 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.
  • 17. Okay... how hard can it be?
  • 18.
  • 19. +
  • 20. +
  • 23. What it is. JavaScript MVC on client & server Backbone & Handlebars BaseView, BaseModel, BaseCollection, BaseApp, ClientRouter, ServerRouter... Express middleware Minimal glue between client & server
  • 25. What it ain’t. Batteries-included web framework Finished
  • 26. 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.
  • 27. Classes: - BaseApp < Backbone.Model - BaseModel < Backbone.Model - BaseCollection < Backbone.Collection - BaseView < Backbone.View - AppView < BaseView - ClientRouter < BaseRouter - ServerRouter < BaseRouter - ModelStore < MemoryStore - CollectionStore < MemoryStore - Fetcher
  • 28. Rendr directory structure |- client/ |- shared/ } Sent to client |- server/
  • 29. App directory structure |- app/ |- public/ |- server/
  • 30. App directory structure } |- app/ |--- collections/ |--- controllers/ |--- models/ Entire app dir |--- templates/ gets sent to |--- views/ |--- app.js client |--- router.js |--- routes.js |- public/ |- server/
  • 31. CommonJS using Stitch On the server: var User = require(‘app/models/user’); var BaseView = require(‘rendr/shared/base/view’); On the client: var User = require(‘app/models/user’); var BaseView = require(‘rendr/shared/base/view’);
  • 32. app/routes.js module.exports = function(match) { match('', 'home#index'); match('users', 'users#index'); match('users/:id', 'users#show'); };
  • 33. app/routes.js module.exports = function(match) { match('', 'home#index'); match('users', 'users#index'); match('users/:id', 'users#show'); }; controller
  • 34. app/routes.js module.exports = function(match) { match('', 'home#index'); match('users', 'users#index'); match('users/:id', 'users#show'); }; action
  • 36. 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
  • 38. 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
  • 39. 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
  • 40. 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
  • 41. app/controllers/users_controller.js module.exports = { show: function(params, callback) { callback(null, 'users_show_view'); } };
  • 42. app/controllers/users_controller.js module.exports = { show: function(params, callback) { callback(null, 'users_show_view'); } };
  • 43. app/controllers/users_controller.js module.exports = { show: function(params, callback) { callback(null, 'users_show_view'); } };
  • 44. app/controllers/users_controller.js module.exports = { show: function(params, callback) { callback(null, 'users_show_view'); } };
  • 45. app/controllers/users_controller.js module.exports = { show: function(params, callback) { callback(null, 'users_show_view'); } }; Most simple case: no fetching of data.
  • 46. app/controllers/users_controller.js module.exports = { show: function(params, callback) { callback(null, 'users_show_view'); } }; But we want to fetch the user.
  • 47. 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); }); } };
  • 48. 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); }); } };
  • 49. 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); }); } };
  • 50. 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); }); } };
  • 51. app/views/users_show_view.js var BaseView = require('rendr/shared/base/view'); module.exports = BaseView.extend({ className: 'users_show_view' }); module.exports.id = 'users_show_view';
  • 52. app/views/users_show_view.js var BaseView = require('rendr/shared/base/view'); module.exports = BaseView.extend({ className: 'users_show_view' }); module.exports.id = 'users_show_view';
  • 53. app/views/users_show_view.js var BaseView = require('rendr/shared/base/view'); module.exports = BaseView.extend({ className: 'users_show_view' }); module.exports.id = 'users_show_view';
  • 54. app/views/users_show_view.js var BaseView = require('rendr/shared/base/view'); module.exports = BaseView.extend({ className: 'users_show_view' }); module.exports.id = 'users_show_view';
  • 55. app/views/users_show_view.js 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';
  • 57. 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>
  • 58. 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>
  • 59. 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>
  • 60. 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>
  • 61. Where’d the data come from? Where’s the render method? How do I customize what gets passed to the template? Sensible defaults.
  • 62. app/views/users_show_view.js var BaseView = require('rendr/shared/base/view'); module.exports = BaseView.extend({ className: 'users_show_view', getTemplateData: function() { } });
  • 63. app/views/users_show_view.js var BaseView = require('rendr/shared/base/view'); module.exports = BaseView.extend({ className: 'users_show_view', getTemplateData: function() { var data = BaseView.prototype.getTemplateData .call(this); } });
  • 64. app/views/users_show_view.js 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() } });
  • 65. app/views/users_show_view.js 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() }); } });
  • 67. Rendered HTML <div class="users_show_view" data-...> <h1>User: SPIKE</h1> <p>From San Francisco.</p> </div>
  • 68. Rendered HTML with layout <!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>
  • 69. Rendered HTML with layout <!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>
  • 71. 1. Find DOM els with data-view attribute. var viewEls = $("[data-view]");
  • 72. 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
  • 73. 3. Fetch model/collection data from ModelStore/CollectionStore. var model = modelStore.get(modelName, modelId);
  • 74. 4. Find view class. var viewName, ViewClass; viewName = viewEl.data(‘view’); ViewClass = require('app/views/' + viewName);
  • 75. 5. Instantiate view instance with model. var view = new ViewClass({ model: model, ... });
  • 76. 6. Attach to DOM el. view.setElement(viewEl);
  • 78. 8. Profit! alert(“That wasn’t so hard, right?”)
  • 79. Rendr is available starting today. github.com/airbnb/rendr
  • 80. 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...