The goal
Reactjs became very popular recently, community grows fast, more and more sites use it, it seems like worth learning then. So I decided to learn.
Although there is so many resources and so many examples over the internet, it’s also hard to learn, especially when you are new to modern frontend stack.
There are examples with ES6 syntax, without, with old react-router syntax, with new react-router syntax, universal ones, not universal ones, with Grunt, with Gulp, with Browserify, with Webpack. I was confused with all that stuff. It was hard to establish what is the minimal set of toolset, I need to achieve my goal.
And the goal was: create isomorphic (a. k. a universal) application with development and production environments (with minified assets in production).
This blog post will be first of the series of blog posts describing my journey while learning modern js tools. It would have a form of tutorial how to create universal app using bare react, then flux and redux at the end.
Why universal? What it means? Do I need this?
So first of all, the easiest way to create React app is to have index.html file with React library included just as regular javascript include.
But I can see examples having it’s own front end server but why would I need the server if I can have just simple html file?
If you just have html file with react included, you can create dynamic modern application and your content will be visible to the user who is accessing your site via browser but it will be rendered only client side. I means that if you open source code, or make a curl request to your site, all you will see is your main div, where the app is injected, but it will be empty.
Of course not seeing it in the source code or making curl request doesn’t probably convince you, but the fact that content of your app will not be visible by Google bots, might. So if you care about SEO, you need to have universal app, which means, app which is not only rendered dynamically client side, but also rendered server side. If you want to achieve this, you need separate server for frontend.
Modern js tools
So my goal was to create separate frontend app with:
- it’s own server
- javascripts written in EcmaScript6 syntax, so something to transpile it to currently implemented in browser one (ES5)
- stylesheets written in Sass, so something to transpile it to css
- all javascripts bundled in one file and all stylesheets bundled in another file
- assets minified for production
- in developent mechanism for watching for changes and transpiling on any
- handled external dependencies
After looking on many examples over internet my mind looked like this:
I didn’t know what these tools do exactly, which of them I need. E. g. Do I need “browser-side require() the node.js way” if I already decided to use ES6? Do I need Bower if I already have npm? Do I need Gulp at all?
After lots of reading I finally managed to group tools:
First choice was quite easy for me, I wanted to write javascripts in new Javasctipt syntax (ES6), so I actually didn’t need coffeescript.
To better understand rest of the grouping I would start from why all these tools are needed.
Module definitions
First of all, we like to write modular code but plain Javascript does not have any native mechanism capable of managing dependencies between files. For a long time the workaround for this was using a mix of anonymous functions and the global namespace:
But it didn’t specify dependencies between files, so developer needed to establish ordering of inclusion such defined modules by hand, which was very error prone.
CommonJS
That’s why CommonJS committee was crated. They described standard for requiring modules that looks like that:
It was implemented in Node, but the fact that the call to require() is synchronous means that it is not well adapted to in-browser use, given that the dynamic loading of the Javascript file itself has to be asynchronous.
AMD
That’s why next standard was created – Asynchronous Module Definition (AMD).
It has some disadvantages though, as time of loading is depended on the latency, so still some people prefer CommonJS synchronous approach. At least until the release of HTTP/2, able to drastically reduce overhead and latency for each single request.
UMD
There is also Universal Module Definition (UMD) standard:
System.register
And another (System.register):
System.register(“bar”, [“foo”], function (_export) {
“use strict”;
var __moduleName = “bar”;
var foo;
function bar() {
return foo(“foobar”);
}
return {
setters: [function (m) {
foo = m.default;
}],
execute: function () {
_export(“bar”, bar);
}
};
});
EcmaScript6 (ES6)
Ok, but I know that there is also “import” keyword in EcmaScript6, so how it’s related?
ES6 is new language syntax. It was standarized in 2014 but it’s not implemented in all browsers yet, so needs to be transpiled by e.g. Babel to ES5 (current JS standard implemented by all browsers).
While setting up Babel you can choose which module definition standard you want to have in the transpiled output. If you don’t choose anything, CommonJS would be chosen by default.
So when you use import keyword from new ES6 syntax, it will be translated to one of the above module definitions.
Module loaders
But it doesn’t mean it will be understand by your environment (browser, Node etc). To actually be able to use modules you defined, you need to have module loader. So e.g. one of following: RequireJS, Almond (minimalistic version of RequireJS), Browserify, Webpack, jspm, SystemJs.
You just need to be aware which module definition is supported by which module loader. E.g RequireJS is supporting AMD, Browserify by default CommonJS, Webpack and jspm both of them, SystemJS all CommonJS, AMD, System.register and UMD.
Dependencies
Next thing is your app usually depends on some libraries. For managing dependencies you can use e.g. Bower or Node package manager (npm). I needed to use Node, to implement frontend server so for me Bower would be just additional dependency.
You should have in mind that when you choose Bower, some of libraries you want to use may be exported as globals. So in order to use them in the application where you choose other module definition standard, you need to wrap them with the abstraction – it’s called a shim – that will make it possible.
You need to check your module loader documentation how to do shimming.
In npm, all libraries have the same format, but of course it can happen, that the library you want to use is not available via npm, but only via Bower.
Task runners
If you use npm, there is scripts block in package.json, where you can define simple tasks.
But if you need to do more tasks to build your app and you want to specify dependencies between them in the nice way, I would recommend to use one of Gulp/Grunt.
Template engines
If you need to have dynamically generated html (to be able to use javascript in your templates), you can use one of templates engines – e.g. Ejs (like erb), Jade (like haml).
Server
Last but not least, I need a server. I know that Node has builtin one, but I’ve also seen examples with Express. Is Express better? What is the difference? The answer is that in Express can can define routing easily:
Ok, but wait, I’ve seen that there is react-router, it seems to be more ‘react way’ to use react routing. Fortunately there is a way to use routing defined using react-router in express.
Here is our hello world example:
Choices
Summing up, my choices where:
- Express for the frontend server
- Webpack for transpiling ES6, JSX, Sass, minifing, bundling to one file, in development watching for changes in files and rebuilding
- No need for Gulp/Grunt, for now using npm scripts
- npm for handling dependencies, as I already need to have Node for a frontend server
But you can of course lot's of other combinations:
plain ES5/CoffeeScript/ES6 + universal(javascript rendered by Node & browser)/client side (only browser) + RequireJS/Almond/Browserify/Webpack/jspm/SystemJs/ + npm/Bower + Gulp/Grunt + plain html/ejs/jade