Slide 1

Slide 1 text

BEYOND THE ASSET PIPELINE Webpack, React, and modular Javascript

Slide 2

Slide 2 text

Ivan Vanderbyl @ivanderbyl github.com/ivanvanderbyl I’m Ivan Vanderbyl, I’m one of the front-end engineers at DigitalOcean, and this talk is largely based on how we moved away from the Asset Pipeline.

Slide 3

Slide 3 text

This is a slide from DHH’s talk at RubyConf 2011 when Rails 3.1 was announced. This was DHH’s vision for the future of Javascript in Rails.

Slide 4

Slide 4 text

DHH's future of JavaScript Asset Pipeline Node.js Today 2011 2012 2013 2014 2015 It was definitely a move forward from the previous practice of vending everything in /public. But it came with a whole new set of problems, and unfortunately the future didn’t work out this way.

Slide 5

Slide 5 text

TODAY Two things happened. We ended up with a lot more javascript in the /app directory, and Node.js came along and taught us that javascript could be composed of small single purpose modules.

Slide 6

Slide 6 text

//= require_tree . //= require_self More javascript. This was largely because require_tree seemed like a good idea at the time

Slide 7

Slide 7 text

// products.js $(function() { ... }); // users.js $(function() { ... }); // account.js $(function() { ... }); // templates.js $(function() { ... }); // controllers.js $(function() { ... }); But all it does is produce a bunch of concatenated javascript named application.js, which typically includes a new DOM ready callback for each function, just to be sure it is safe to run and manipulate the DOM.

Slide 8

Slide 8 text

This doesn’t just load on every page, it executes on every page. This is a mess. This doesn’t just load on every page, it executes on every page. This in turn has page load performance implications.

Slide 9

Slide 9 text

The Asset Pipeline never took Javascript seriously The Asset Pipeline never took Javascript seriously

Slide 10

Slide 10 text

And maybe that’s because DHH doesn’t take Javascript seriously.

Slide 11

Slide 11 text

We don’t get paid to write lines of beautiful Ruby code We get paid to build products which customers love you don’t get paid for the aesthetics of your code. You get paid to delivery great products, and part of any great product is a great user experience. And the expectation is quickly becoming that any great user experience will involve javascript. So let’s treat it as a first class citizen in our applications.

Slide 12

Slide 12 text

Great products require great user experiences you don’t get paid for the aesthetics of your code. You get paid to delivery great products, and part of any great product is a great user experience. And the expectation is quickly becoming that any great user experience will involve javascript. So let’s treat it as a first class citizen in our applications.

Slide 13

Slide 13 text

Great user experiences are built on friendly interactions you don’t get paid for the aesthetics of your code. You get paid to delivery great products, and part of any great product is a great user experience. And the expectation is quickly becoming that any great user experience will involve javascript. So let’s treat it as a first class citizen in our applications.

Slide 14

Slide 14 text

Javascript often plays front-stage role building interactions you don’t get paid for the aesthetics of your code. You get paid to delivery great products, and part of any great product is a great user experience. And the expectation is quickly becoming that any great user experience will involve javascript. So let’s treat it as a first class citizen in our applications.

Slide 15

Slide 15 text

Let’s take JS more seriously you don’t get paid for the aesthetics of your code. You get paid to delivery great products, and part of any great product is a great user experience. And the expectation is quickly becoming that any great user experience will involve javascript. So let’s treat it as a first class citizen in our applications.

Slide 16

Slide 16 text

The what and how of modular Javascript I’m going to talk about how to use modular JS with Rails, and then show you an example React component App.

Slide 17

Slide 17 text

// component_a.js // ES6 Module Definition import MyDependency from './my_dependency'; var ComponentA = MyDependency.extend({ ... }); export default ComponentA; Let’s look at the future. This is an ES6 module. ES6 is the next generation of JS which is still largely in a proposal state, but some parts can be transpiled to ES5 today. Like this module.

Slide 18

Slide 18 text

Isolated Code The first thing modular JS gives us is code isolation. Everything inside a module will only ever be declared once, and re- used everywhere you require it.

Slide 19

Slide 19 text

No Globals You can’t declare globals inside modules. This is a good thing.

Slide 20

Slide 20 text

Asset compilers can remove dead code paths Compilers can remove code which never gets called, keeping your compiled assets small. Only use a portion of a framework, instead of loading everything.

Slide 21

Slide 21 text

Package Management Share libraries using NPM or Bower.

Slide 22

Slide 22 text

Test everything in isolation Testing can be done client side, inside a headless browser, or on the server, because it’s all isolated. This makes testing more consistent with other languages. You don’t need to spin up the whole stack to run your JS tests

Slide 23

Slide 23 text

webpack Lets talk about Webpack. It replicates some of the features of the AssetPipeline, but it adds support for everything I’ve just mentioned, and I’m going to show you how to use it with Rails.

Slide 24

Slide 24 text

Webpack takes a bunch of modules with dependencies, and compiles them to static assets.

Slide 25

Slide 25 text

app/assets/javascripts ├── components │ └── ... ├── _application.js ├── _vendor.js ├── application.js └── vendor.js So we might have a structure like this, notice we have some underscored prefixes on application and vendor. These are our webpack entry point files. Everything in here will be compiled to a bundle file inside vendor/assets/javascripts

Slide 26

Slide 26 text

// _application.js /** * Application Entry Point * * @type {React Component} */ window.App = require('./app').default; Inside _application.js we might have some requires like this. This is all valid javascript, using the commonJS require conventions, which will be picked up by Webpack

Slide 27

Slide 27 text

// _vendor.js /** * Globally require anything which * we want to include in vendor.bundle and * webpack will package it here instead * of the application.bundle. */ require('react'); require('react/addons'); One of the really useful features of webpack is chunking. You can create a vendor chunk which just loads the libraries, so you can have a large vendor bundle which changes rarely and a small app bundle which changes often, which is ideal for CDN caching.

Slide 28

Slide 28 text

// app/assets/javascripts/application.js //= require application.bundle // app/assets/javascripts/vendor.js //= require jquery //= require jquery_ujs //= require vendor.bundle Inside the actual vendor.js we just tell the Asset Pipeline to load our compiled static bundle, and the same in application.js

Slide 29

Slide 29 text

// webpack.config.js var webpack = require('webpack'); module.exports = { context: __dirname + '/app/assets/javascripts', entry: { application: './_application.js', vendor: './_vendor.js', }, output: { filename: '[name].bundle.js', path: __dirname + '/vendor/assets/javascripts', }, }; The Webpack config to make all this work is actually quite simple, we have an entry point which loads our modular JS requires, and an output, which dumps the static assets.

Slide 30

Slide 30 text

$ webpack --compile --watch Hash: 23e1444af49a5c660291 Version: webpack 1.4.10 Time: 5780ms Asset Size Chunks Chunk Names vendor.bundle.js 648825 0 [emitted] vendor application.bundle.js 301145 1 [emitted] application + 201 hidden modules We can then run the webpack command line tool and output our JS bundles ready for rails to load. There’s also a handy — watch flag to have webpack recompile when things change.

Slide 31

Slide 31 text

$ npm install moment —save-dev var moment = require(‘moment’); We can now install external modules and use them with Webpack.

Slide 32

Slide 32 text

$ npm install react —save-dev var assign = require(‘react/lib/Object.assign’); Or use a single component from a package.

Slide 33

Slide 33 text

http://webpack.github.io/docs/testing.html Testing For time I won’t go in to testing here, it’s a whole topic in itself. But you can either test webpack modules in the browser, or on the server with Node.js, and here’s the Webpack testing guide.

Slide 34

Slide 34 text

React.js This talk was actually going to be all about React and Flux, but ultimately it made sense to cover Webpack, because without it using React with Rails is not fun

Slide 35

Slide 35 text

React.js The V in MVC… sort of React is not a framework, it’s just the View layer, and the rest is left up to you to implement.

Slide 36

Slide 36 text

React powers the view layer of Facebook It was created at Facebook to power large parts of the Facebook front-end, and all of the Instagram web experience

Slide 37

Slide 37 text

React is a… Highly performant component centric View layer. React is a highly performant view layer, it is not a framework, and it doesn't handle your business logic.

Slide 38

Slide 38 text

Built on isolated Components React is just a bunch of components

Slide 39

Slide 39 text

Components have internal state setState({ ... }); Each component has its own internal state and properties, nothing more. When the state changes, React renders a virtual DOM representation of the component based on that state.

Slide 40

Slide 40 text

Virtual DOM ey? Each component has it’s own render method which returns virtual DOM nodes, but you never need to call this. It’s used internally by React in response to setState.

Slide 41

Slide 41 text

Virtual DOM Diffing http://facebook.github.io/react/docs/reconciliation.html The real power of React is the DOM diffing, or reconciliation. Once we have a virtual DOM output, React diffs this against the real DOM, then merges the changes with the real DOM, very efficiently. You can read about the crazy heuristics which make this work

Slide 42

Slide 42 text

setState(); Virtual DOM Reconciliation() DOM Render(); This is the flow. You can setState somewhere in your component, which in turn ensures that your DOM is consistent with state you expect.

Slide 43

Slide 43 text

Your component’s state is always represented in the DOM Your component is always represented in the DOM, no separation of templates and view logic, because they should be tightly coupled, because that’s how they usually get used.

Slide 44

Slide 44 text

Re-render the whole application on every change React is designed so you can re-render your whole application on every change

Slide 45

Slide 45 text

Isn’t that really slow? No.

Slide 46

Slide 46 text

Turns out Javascript is kind of fast Javascript is actually quite fast, and this is rarely the bottleneck.

Slide 47

Slide 47 text

module React from 'react'; var MyComponent = React.createClass({ render: function() { return
Hello {this.props.name}
; } }); export default MyComponent; Here’s an example component. Notice the weird inline HTML stuff, isn’t that crazy?

Slide 48

Slide 48 text

module React from 'react'; var MyComponent = React.createClass({ render: function() { return React.createElement("div", null, "Hello ", this.props.name); } }); export default MyComponent; That’s JSX, which you can compile on the server or the client, or not use at all, because it only gets converted to JS.

Slide 49

Slide 49 text

render: function(){ }, You can render other components just like HTML tags. Very consistent API.

Slide 50

Slide 50 text

ParentComponent MyComponent Unidirectional Data Flow state = { … } You can treat the state of your top level component as the definitive source of truth and just re-render the whole UI every time something changes. This enforces an easy to reason about single direction of data flow.

Slide 51

Slide 51 text

So an example UI might be composed like this. Your life with React will be significantly easier when each component is kept as simple as possible.

Slide 52

Slide 52 text

It’s best if components only do one thing It’s best if components only do one thing. This makes them easier to understand, and easier to reuse in other components.

Slide 53

Slide 53 text

var MyApp = React.createClass({ getInitialState: function(){ return { items: [ {name: "Item 1"}, {name: "Item 2"}, ]}; }, render: function() { var listItems = this.state.items.map(function(item) { return ; }); return (
    { listItems }
); } }); One thing I really like about React is that you can use standard javascript operators to handle composing your UI. Example here we use map on an array of items to return another array of item components, which we then render in a list

Slide 54

Slide 54 text

clickHandler: function(){ var items = this.state.items; items.push({ name: "New Item" }); this.setState({ items: items }); }, render: function() { var listItems = this.state.items.map(function(item) { return ; }); return (
    { listItems }
Add Item
); } }); This makes adding new items to our list as simple as updating the state, in this case just pushing a new item on to the array.

Slide 55

Slide 55 text

React is not a framework Only a view layer

Slide 56

Slide 56 text

So.. what about business logic? So if components aren’t meant to contain business logic, where do we put this?

Slide 57

Slide 57 text

React with Backbone One obvious solution is to use React with Backbone, which I’ve heard of people having a lot of success with.

Slide 58

Slide 58 text

React with Flux However, one more interesting approach is Flux, a data flow pattern which facebook uses to make it easier to reason about the state of their application

Slide 59

Slide 59 text

http://facebook.github.io/react http://facebook.github.io/flux

Slide 60

Slide 60 text

We’re hiring