the evolution of airbnb's frontend
TRANSCRIPT
1. A Brief History of airbnb.com 2. Modernizing our JavaScript Stack 3. Airbnb & Isomorphic JavaScript
Rails 3.x, Java, Node.js
MySQL, Redis, HDFS, Postgres, DynamoDB, Kafka, RabbitMQ, …
SASS, O2
jQuery, Backbone, Handlebars, React, CommonJS, ES6
!2015
Hitting a Wall with Backbone & Handlebars
Backbone & Handlebars approach falls apart as UI components become more dynamic.
No data binding, so have to either:
1. Re-render whole view when model changes (slow, lose DOM state like focused fields).
2. Drop down to manual DOM manipulation.
Airbnb <3 React
React makes it easier to build & maintain stateful UI components.
React makes it easier to reason about data flow in your app.
Sprockets
Rails asset pipeline: manages JavaScripts, stylesheets, images, etc.
Written by Ruby devs to solve problems of simple web apps.
JavaScript </3 Sprockets
Doesn’t solve JavaScript dependency management:
1. Local dependencies (application code)
2. External dependencies (third-part libraries)
/* application.js *///= require_tree .
/* application.js */!//= require ./models/listing.js//= require ./collections/listings.js//= require_tree .
What if one file depends on another?
Bad: Order matters Bad: Objects attached to `window` Bad: Implicit dependencies
Local dependencies
$ cp ~/Downloads/backbone.js app/assets/javascripts/vendor/ backbone-1.1.12.js
/* application.js */!//= require vendor/backbone-1.1.12.js...//= require_tree .
What if I want to use Backbone?
Bad: Manually download Bad: Manually versioning Bad: Implicit dependencies
External dependencies
/* collections/listings.js */var Listing = require(‘../models/listing’);!module.exports = ...
Good: Explicit dependencies Good: Don’t worry about ordering Good: Objects not leaked to `window`
Local dependencies
/* collections/listings.js */var Listing = require(‘../models/listing’);var Backbone = require(‘backbone’);!module.exports = Backbone.Collection.extend({ ...});
Good: Central management Good: Explicit dependencies
External dependencies
$ npm install --save [email protected]
Browserify*
Use CommonJS syntax in client-side modules: `require` and `module.exports`.
Package dependencies from NPM.
Transforms.
* or Webpack
Browserify Transforms
Handlebars var template = require(‘./templates/user.hbs’);!var html = template({name: “Spike”});
es2015 import Header from ‘./Header.jsx’;!const numMonths = 36;!let photos = users.map((user) => user.photo);
PerformanceInitial pageload speed.
SEO*Crawlable single-page apps.
FlexibilityRun code anywhere.
MaintainabilityReduce code duplication.
Client-rendered appDownload skeleton HTML
User sees content
Download JavaScript
Fetch data from API
Evaluate JavaScript
Exacerbated on mobile: high latency, low bandwidth
10%
90%
Rails Node.js
…but too hard to share code between server and server — between Rails and Node.js.
Long tail of features to rewrite in JavaScript:
1. CSRF tokens 2. Custom headers, i.e.
CDN 3. Middleware 4. Application code
The single worst strategic mistake that any software company can make: !Rewrite the code from scratch.
Joel Spolsky
Mystique: Node.js rendering service
for React components
Allows us to render React components on the server from within Rails.
Built on Iso, tiny utility for isomorphic bootstrapping of React components. https://github.com/goatslacker/iso