I have always thought of myself as a backend developer. Sure, I can whip up some HTML and CSS to get a <div> to sit where I want it, or I can make clicking one thing cause some others to disappear and different things fall into place. But anything beyond what jQuery and an hour of Googling could do for me has always seemed out of my league.
Lately, I've been wanting to brush up on the other end of webdev and round out my skills a bit more. This is the first in a series of posts I will be writing along the way, documenting what I've learned and what worked well for me.
Rails is the easy part. I generated a blank Rails app with the default generator:
rails new orochi
The main thing that I wanted to try out was Facebook’s React library. From what I had read and the code samples I had seen, it all looked pretty cool. This coupled with some hype over it at my day job had me excited to try it out.
Facebook provides a react-rails gem that offers some good integration with the Asset Pipeline. It also does some cool stuff with allowing you to render your React components on the server to get a faster initial page load. This was where I went first when looking for React/Rails integration, but I ended up passing on it for a few reasons that I’ll get to below.
Webpack does this by recursively analyzing your code and its dependencies for
<script> tag in the browser.
A second, and in my opinion much cooler, benefit of having Webpack process your files is that Webpack can do much more than just concatenate. Through a system it calls Loaders, Webpack allows you to specify different transformations that you want to be applied to code that gets loaded. For example, if you had some Coffeescript code, using the
coffee-loader and a little configuration, you could simply write
Webpack also allows you to split the resulting concatenated code into multiple files. This is useful if you have a large library that only part of your code uses. You can split this library off into its own file, called a "chunk" in Webpack's jargon, that would get loaded asynchronously when your code requests it. While I haven't found a use for this feature personally, I can definitely see how it could be useful in different situations.
Browserify is a module bundler, like Webpack. It also inspects your code for
require calls, lets you transform your code with transforms, and concatenates your code into a single bundle file that can be included from the Browser.
One thing I found that Browserify offers over Webpack is the browserify-rails gem. With this gem, Browserify gets some pretty deep integration into the Rails Asset Pipeline, no extra tools required.
The biggest difference for me between Browserify and Webpack is that Webpack's loaders feel much easier to use and more configurable than Browserify's transforms. Browserify transforms allow you to specify a particular extension that they will be applied to. Webpack's config, by contrast, allows you to specify not just one loader, but a whole pipeline of loaders that feed into each other. Webpack also allows you to specify a regular expression to test paths against, rather than just a string extension.
In the end, through Browserify has browserify-rails, I decided to go with Webpack for the superior configurability.
Webpack and React are both available over NPM for easy installation:
npm install --save webpack react
--save option will save the dependencies to your
I also installed Webpack globally so I could have access to its command line tool:
npm install -g webpack
--global) option tells NPM to install the package to your global
node_modules folder and also to install the package's executables to your
Plugging into the Asset Pipeline
The trickiest part was figuring out how to integrate Webpack with the Asset Pipeline. I had a few goals:
- I should be able to run Webpack in watch mode and let it recompile my code.
- I should be able to refresh my browser to see the changes.
- I want to be able to reference packages in
Here's the config I came up with
There's a lot in here, so I'll take it piece by piece.
First, in the
resolve section, I've set
['', '.js', '.jsx'] to
extensions which tells Webpack that it should try those extensions when trying to resolve a module name without an extension (such as
./entry.js. This tells Webpack that the first file it should start with when compiling my assets is going to be called
root setting from above lets Webpack find this file in
output section I have set the
bundle.js and the path to
application.js manifest file used by Sprockets in the Asset Pipeline.
module section I have configured a few loaders. I'm using the 6to5-loader to transpile my .js and .jsx files using 6to5. This is great because it allows me to write code using ES6 syntax, including generators and arrow functions, and compile that code down to ES5 for use in the browser. It also lets me use Facebook's JSX syntax when constructing React components, as 6to5 will automatically compile that as well.
externals I have
jquery: var jQuery. This is because I want to include the jQuery that Rails bundles through the jquery-rails gem, rather than installing it into
node_modules. Doing that makes jQuery an "external" dependency in the eyes of Webpack (because it's not being compiled by Webpack), but I still want to be able to use
require to bring jQuery into my scope when I need it. This setting lets Webpack know that when I write
require('jquery'), it should replace that with a reference to the external global var
Finally, I export the
config object in
module.exports. This lets me load the config into Webpack by using just
With that config settled, integration to the Asset Pipeline is simple.
This is just the standard
application.js manifest file, with one difference. I've removed the
//= require_tree directive and replaced it with
//= require bundle. Now Sprockets will no longer pull in all of the files in
As we established with the Webpack config above, that
Getting set up with Gulp was pretty simple. I installed it to my project and also globally to get the executable:
npm install --save gulp npm install -g gulp
To use Webpack from Gulp, I needed to the
gulp-webpack package as well:
npm install --save gulp-webpack
And finally, I tied it all together with this
This does a few things.
First, it defines a
gulp webpack task that pipes the
webpack filter (loaded with the config from above), and puts the output into the
Next, it defines a
gulp watch task that watches specific files in the project for changes, and then runs the
gulp webpack command when a change is detected. This actually took a little tweaking, because my source and output directories were both
Finally, it defines the
default task and makes that execute the
gulp watch command.
With all this, I'm writing modular code, enjoying the new ES6 features, and experimenting with React, all within a very basic Rails 4 app.
Look forward to my next post in this series, where I will talk about using FRP with React to handle UI events!