On Organizing JavaScript and CSS Assets Within MVC Frameworks

Posted:

Web Pyramid via @pinboard on Twitter

(h/t to @pinboard on Twitter for posting this pyramid graphic)

When I develop web apps (which these days are generally small, intranet business apps), I use Perl 5.2x and Mojolicious for the core stack. MySQL, apache and my custom ORM are also part of that stack, but let’s leave that for a future post.

My front-end JavaScript and CSS is fairly small: Bootstrap 4.x (which currently requires jQuery), and Font Awesome 5. Because of all the wonderful things ECMAScript 6 has brought, I have been weening myself off jQuery, quite successfully.

Understand that my web apps are not Single Page Applications (SPA). I have no requirements to make such apps and I have always found them problematic. There is nothing quite like a browser page refresh to re-establish a sane web app state.

For a while, I have been using the trick of adding the current controller name to an outer wrapper in the layout, something like this:

  <!doctype html>
  <html lang="en">
  <head>
     <meta charset="UTF-8"/>
     <title>Document</title>
  </head>
  <body>
     <div class="container controller $THIS_CONTROLLER">
       $VIEW_CONTENT
     </div>
  </body>
  </html>    

Since the layout is implemented by a server-side templating engine (usually Template Toolkit), it is easy to get the name of the current controller and insert that into the layout even before the view-specific content is available.

Why do this? Scoping you DOM in this manner makes it trivial to scope CSS elements to specific views. Image I have a controller called Users. I might have controller-specific CSS that looks like this:

  h3 { font-size: 1.5 rem; color: yellow }
  .Users h3 { font-size: 120%; color: pink; font-style: italic }

This ensures that all the H3 tags on any view generated by the Users controller appears larger, pinker and slantier than H3 tags found on other pages.

Scoping CSS brings a great deal of structure to CSS quite easily.

ECMAScript 6 defines modules, which create a compartmentalization of code long lacking in JS. The top level HTML makes a top-level JS script as a module like this:

  <script type="module" src="./top-level.js"></script>

Inside of that top level JS script, you can use import statements (which syntactally borrow a lot form python) to pull in other JS modules using the Fetch API. Finally, ECMAScript 6 introduces a class statement along with an extends mechanism so that you never have to think about prototype again.

Just like CSS can be scoped to controllers, it occurred to me that JS modules can be scoped to controllers!

  <!doctype html>
  <html lang="en">
  <head>
     <meta charset="UTF-8"/>
     <title>Document</title>
  </head>
  <body>
     <div class="container controller $THIS_CONTROLLER">
       $VIEW_CONTENT
     </div>
     <script type="module" src="js/$THIS_CONTROLLER.js"></script>
  </body>
  </html>      

Common code can be factored into classes used by multiple controller-level modules. This brings a tremendous amount of structure and sanity to web apps that have long been missing.

Hope this inspires you to try out this pattern in your own projects.