ReactJS has become very popular recently, the community is growing fast and more and more sites use it, so it seems like something worth learning. That’s why I decided to explore it.
There is so many resources and so many examples over the internet that it’s difficult to wrap your head around it all, especially when you are new to the modern frontend stack.
There are examples with ES6 syntax, without ES6 syntax, with old react-router syntax, with new react-router syntax, examples with universal apps, with non-universal apps, with Grunt, with Gulp, with Browserify, with Webpack, and so on. I was confused with all of that. It was hard to establish what is the minimal toolset needed to achieve my goal.
And the goal was: to create a universal application with development and production environments (with minified assets in production).
Why universal? What does it mean? Do I need this?
It seems easy, so why have I seen example applications which have their own frontend servers? I started wondering why would I even need the server if I can just have a simple HTML file.
And the answer is: sure, you can create a modern dynamic application just by using simple HTML file, but you need to keep in mind that it’s content will be rendered on the client side only.
It means that if you view the page source in the browser, or make a curl request to your site, all you will see is your main div where the app is injected, but the div itself will be empty.
If the above doesn’t convince you, then perhaps this will: Google bots won’t see the content of your app if it’s only rendered on the client side. So if you care about SEO, you should definitely go with a universal app – an app which is not only rendered dynamically on the client side but also on the server side.
To achieve this you need a separate server for frontend.
You can see people referring to these kinds of apps as isomorphic. Universal is just a new, better name.
My goal was to create a separate frontend app with following characteristics:
- JS scripts written in EcmaScript6 syntax. So we need something to transpile ES6 to ES5 (ES6 is not fully supported in browsers yet).
- Stylesheets written in Sass. So we need something to transpile SASS into CSS.
- Assets (js, css) minified for production.
- A mechanism to watch for changes and transpile on the fly in development mode, to speed up work flow.
- Something to handle external dependencies.
After looking at many examples on the Internet, my mind looked like this:
I didn’t know what all of these tools do exactly and which of them I needed. 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 the tools:
As ES6 introduces lots of convenient features which will soon be implemented in browsers, in my opinion there is no need to use CoffeeScript right now. That’s why I choose to use ES6.
One of many convenient features of ES6 is the ability to define modules in a convenient and universal way.
Unfortunately, it didn’t specify dependencies between files. The developer was responsible for establishing the correct order of included files by hand.
As you can suspect, it was very error prone.
That’s why the CommonJS committee was created with a goal to create a standard for requiring modules.
To solve this problem, a next standard was proposed – Asynchronous Module Definition (AMD).
It has some disadvantages, though. Loading time depends on latency, so loading dependencies can take long too.
Incoming HTTP/2 standard is meant to drastically reduce overhead and latency for each single request, but until that happens, some people still prefer the CommonJS synchronous approach.
While setting up Babel you can choose which module definition standard you want to have in the transpiled output. The default is CommonJS.
So when you define you module in new ES6 syntax:
It will be translated to the chosen standard.
If you’ve chosen CommonJS the above module would be transpiled to:
And for AMD:
To make it work in the environment of your choice (browser, Node.js etc.) you need to use a module loader. So module loader is a thing that loads your module definition in the environment.
You just need to choose one and follow the documentation on how to define your modules.
For example, RequireJS supports the AMD standard, Browserify by default CommonJS, Webpack and jspm support both AMD and CommonJS, and SystemJS supports CommonJS, AMD, System.register and UMD.
Your app usually depends on some libraries. You could just download and include all of them in your files, but it’s not very convenient and quickly gets out of hand in larger projects.
There are a few tools for dependency management. If you use Node.js, you are probably familiar with it’s package manager – npm.
Another very popular one is Bower.
Since I needed to use Node.js to implement the frontend server, I decided to go with npm.
In npm, all libraries are exported in 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.
In such chase remember that some of the libraries may be exported in a different format than what you’re using in your application (e.g. as globals).
In order to use those libraries, you need to wrap them in some kind of adapting abstraction. This abstraction is called a shim.
Please check your module loader documentation how to do shimming.
If you use npm you can define simple tasks in your top-level package.json file.
It’s convenient as a starting point, but if your app grows it may not be sufficient anymore. If you need to specify many tasks with dependencies between them, I recommend one of popular task runners such as Gulp or Grunt.
Last but not least I need a server. Node.js has a built-in one, but there is also Express.
Is Express better? What is the difference? Well, with Express you can define routing easily:
It looks really good, but I’ve also seen many examples using routing specific to ReactJS – implemented with react-router.
I wanted to use react-router too, as it seems more ‘ReactJS way’. Fortunately there is a way to combine react-router with Express server by using match method from react-router.
Summing up, here are my choices matched with characteristics that I defined at the begging of this post:
- JS scripts written in EcmaScript6 syntax – transpiling ES6 to ES5 using Babel loaded through Webpack
- Stylesheets written in Sass – transpiling SASS into CSS using sass-loader for Webpack
- Assets (js, css) minified for production – Webpack
- A mechanism to watch for changes and transpile on the fly in development mode, to speed up work flow – Webpack
- Something to handle external dependencies – npm
Additionally I chose Ejs for the layout template and since I’m using npm and Webpack we don’t really need to bother with grunt or Gulp task runners.
But of course, you can choose differently since there is a lot of other combinations:
Now that we know what we want to use, in the next post we will move on to creating the app. See you next week!
Update: Here is the next post.