Adventures in the front-end wonderland
webdev angularjsHello, I am a back-end engineer and happily shuffle the bits in the server side. In the last couple weeks, I’ve been wearing the front-end engineer hat and I’d like to share my experience and rants.
Déjà vu
The trend of the front-end work is the so-called “JavaScript MVC frameworks”, represented by AngularJS and futuristic Polymer. The idea is to shift the state transitions from the back-end to the front-end, and the back-end only provides the data access via APIs. This architecture has several major benefits over the traditional Model-View-Controller model:
- The state transition is seamless, no more redirect or reload.
- The back-end is less stateful, easier to scale.
- The API may also be consumed by the mobile app, a true “mobile first” paradigm.
Does this architecture look familiar? In the nineties, it is called client/server model. Despite the similarity, the JavaScript MVCs have huge advantages:
- Timing. The personal computing power and network performance have significantly improved over the last two decades. It is feasible to shift the complex logic to the front-end.
- The deployment and upgrade process are much smoother for the end-users and IT department.
Down the Rabbit Hole
The cool kids in the block are using the trio, yeoman, bower, and grunt:
Bower is a front-end assets manager tool, just like the npm for the JavaScript packages in the back-end. You can install various front-end packages in a bliss:
bower install angularjs bootstrap font-awesome --save
The assets and their dependencies are pulled and copied into local
bower_components
folder for later use. With --save
option, the meta data is
persisted in bower.json
for bookkeeping.
The real magic comes from grunt:
- with
grunt-bower-install
plugin, the bower dependencies are automatically injected into the html page. - with
grunt-asset-injector
plugin, the individual assets are bundled to minify the HTTP requests. - with various minify plugins, the css, JavaScript, html, svg, png, gif, jpeg, etc are minified.
- with
grunt-rev
, the minified assets are revisioned to be cache-friendly. - with
grunt-contrib-watch
, you modification will be trigger the build action and reload the server. - with
gulp-swig
, the static marketing pages are rendered from the template.
Yeoman is a scaffolding tool to glue them together. There are hundreds generators to bootstrap a project. I can get a working MEAN(MongoDB, ExpressJS, AngularJS, NodeJS) stack in less than 5 minutes thanks to the trio.
There are some other technical decisions you have to make, here are mine with some notes:
- JavaScript MVC: AngularJS.
- CSS Framework: Bootstrap.
- The backend: NodeJS and ExpressJS. I generally prefer python and Flask, but the context switch between JavaScript and python is counterproductive, and the backend is extremely simple.
- Template engine: swig. Both swig and jade support template inheritance, I prefer swig as it looks similar to Jinja2.
Lessons Learned
I believe the best way to learn a new thing is to just get your hands dirty. The web application I’ve been working on is a tiny but full-fledged Software-as-a-Service:
- It has a home landing page with all the marketing bells and whistles. They are plain old static html pages for the SEO juice.
- The AngularJS application requires the authentication, provided by the 3rd party via OAuth.
- The data is provided by another service, which is temporarily out of the scope. I will use some canned content for the development.
This seems a reasonable and realistic project, isn’t it? But the problems I encountered slowed down the progress so much that I would have been fired if I were paid to do so. Here are some lessons I have learned:
Less is more
The first mistake I have made is taking a bigger bite than I can digest. I bootstrapped the project via angular-fullstack with all the shining add-ons, then tried to trim it down to fit my needs. It didn’t work very well due to the lack of knowledge of the conventions of the grunt plugins. I ended up switching to gulp.js for its explicit pipeline approach, and bootstrapped the project with minimum boilerplate code, then add component gradually. I think this approach make the project more manageable.
Pragmatic reuse
We all know the DRY(Don’t Repeat Yourself) principle, but sometimes we just have
to bend for the reality. For example, the server views and the home landing page
could share the common boilerplate template, but it is tedious to configure the
search path for gulp-useref
plugin correctly. I eventually separated the two
base templates for each use.
Always use absolute path
The front-end engineers tend to use the relative path to reference the assets, so the html page can be opened locally without a web server. It is OK if you are building an one page application without backend; otherwise explicit is always better than implicit If you use Mac, you might checkout the cool Anvil app.
The back-end is almost inevitable
The back-end is almost inevitable unless you stick to the old school username/passport approach which can be done in purely asynchronous fashion:
- The web application is loaded from the web server
- The authentication is issued in the AJAX POST request to the Auth server
- The Auth server verify the password, and sign the meta data with JSON Web Token.
- The web application then save the token in the browser session and talk to the API server.
If you use OAuth, the authentication is initiated in the server side and finished with the server side callback. The back-end then set cookie or render a page with signed JWT; and the client continue with step 4 as specified above.
Be aware of template conflict
There may coexist three templates in your web application:
- the server side template, swig in my case
- the angular template
- the legacy mustache template
They all pretty much look the same! If the client side template goes through the server render pipeline mistakenly, you may see weird rendering bug in the client side. My suggestion are:
- use a dedicated web server, such as nginx to server the static content in the production.
- use a restrictive glob pattern in you gulp.js or Gruntfile.js to avoid such mistake.
Conclusion
This post is more about the tools and build system, it barely scratches the surface of the front-end engineering. I struggled for the best practice for several nights to understand what I have in the toolbox and I hope it is valuable to you as well.