For the many things that I think I got right in MarionetteJS, there are a number of #facepalm things and “WHAT THE?!?!?” things… the most notable being the module system. Let’s face it, this is probably the clunkiest module system ever written – and all because I hate AMD (Asynchronous Module Definition) and RequireJS. It seems no matter how many times I try to use RequireJS, it just gets in my way and makes my life painful. I don’t know what it is… I’ve tried. and tried. and cried and tried and cried. I just don’t like AMD. So I wrote something worse and called it good enough.
To everyone that has ever used Marionette’s modules: I’m sorry. I should have known better (and probably did, but didn’t listen to myself or anyone else). I hope the rest of Marionette makes up for that horrible mess I made.
But I’m officially over Marionette’s modules, now. I’ve found something better. Something I actually enjoy working with. Something that fits the way I think a module system should work, without any of the stupid hacks and work arounds that I’ve become used to.
What I Love About Browserify
There’s one thing that sold me on browserify: the ability to use what look like NodeJS / CommonJS modules in my browser. I’ve been working in NodeJS for the last few years now, and I’ve really come to appreciate the simplicity of the NodeJS module system… the ability to require any file in to my current file, or a module in to my current file; the ability to module.exports any object or data or function that I want, out of a file – these are all things that I like. And I almost immediately fell in love with browseify when I realized I could write code that looks like NodeJS modules, but have it run in my browser!
You can run these two files in both NodeJS and in a browser. The difference is that you have to run the browserify command to bundle the files together before running it in a browser.
Browserify does it’s magic through the “require” keyword, and builds a final output file that contains everything the browser needs. Epic win!
“But that’s not so different than AMD / RequireJS!”
I know it’s not that much different, but it is different enough. The way AMD and RequireJS typically work adds a lot of extra cruft around each of the individual files, though… boilerplate that you, the developer, have to put in to each file. Browserify ends up with a similar module pattern wrapper around each file, but it does it for you. It’s transparent to the developer, so you never have to worry about it. I like that. It makes me happy to get rid of that boilerplate.
I also get to avoid all of the add-ons and plugins and crazy configuration shims that RequireJS tends to … require (see what I did there?). And avoiding crazy configuration makes me happy, too. I’ve spent days on end trying to configure RequireJS properly, with the right plugins and shims, slamming my face in to my keyboard in frustration. Sure, I eventually got it to work, but not before Jim Cowart had to write a 10 page article on how to properly shim things that I was using at the time.
My experience with RequireJS and AMD has never been pleasant. Maybe yours has. Good for you. I don’t like it. I like browserify.
How I’m Using It With Backbone
Ok, so browserify isn’t absolutely perfect. I don’t claim it is. I only claim it’s better than AMD. But there are still a few tricks to it, unfortunately – loading libraries like Backbone, as a prime example. In my (somewhat limited) experience, there are 3 ways to load Backbone (and underscore, and jquery, and marionettejs) in to my browserified app.
- just let Backbone be a global var, like always
- use the NPM backbone package
- magic hacks with browserify externals (i think)
I’m tending to go with option #1 these days, cause it suits my way of thinking and my style of development at the moment. I end up with at least 2 <script> tags because of this, but I’m ok with 2 for the apps i’m building right now. They tend to look like this:
where the “infrastructure.js” is a concat & minified file containing underscore, jquery, backbone, marionettejs and any other “infrastructure” libraries that don’t change very often. The “app.js” file is the browserified bundle. It’s where my actual app code lives, after running the browserify command line tool to build the bundle.
Option #2 is probably a “better” idea in that everything will get bundled in to the one app.js file. I haven’t bothered to set this up in my apps, but it seems simple enough. You can google for “browserify backbone.js” and find a few hundred articles on how to set this up using the NPM packages for Backbone and jQuery.
Option #3 is something I’m trying to figure out in my spare time (which I have none of right now). I think there should be a way to use one of browserify’s options to load external libraries in to the bundle without having to use the NPM package. The reason you would need to do this, is that Backbone detects the presence of a “require” function, and when it sees that it puts itself in to module-mode. This means it tries to “require” it’s dependencies (underscore) instead of looking for a global. This causes browserify to have problems loading backbone if you’re not using the backbone NPM package. I think there’s a way around this, but I haven’t figured it out yet (other than option #1 or #2).
How I’m Using It With MarionetteJS
In addition to Backbone, I’m using MarionetteJS with browserify in my app. Once again, Marionette ends up inside of the infrastructure.js file, referenced above. The difference between what I’m doing now and what I used to do, then, is in the module system.
Previously, I used Marionette’s module system to private encapsulation and modules, while also providing the ability to have “sub-applications” in my system. That is, smaller part of the over-all system that could be started and stopped when needed. Having moved to browserify, I’m no longer using Marionette’s modules. This means I no longer have access to the Application object from inside of my modules, and I no longer have the ability to start / stop sub-applications as needed (at least, not built-in).
The one major problem I have with no access to the Application object, is getting to the root level regions. I solved this by adding a “getRegions()” function to my Application object, and use that to pass the list of regions down through my application hierarchy. It’s a bit ugly, but it works fine for now. I’m hoping to find a more elegant solution for this at some time, though.
The problem with no sub-applications to start / stop has not yet presented itself in my apps. I don’t need that right now. I know I will need it, though, and when I do run in to that requirement again, I’ll find another solution for it. There’s a good chance I’ll build an add-on for Marionette that specifically works with this new setup, in order to provide the functionality I want. There’s also work being done in Marionette’s future versions to allow multiple Application instances, which would help facilitate this.
The Gory Details Of Using It With Marionette
Jason Krol posted a link to his epic blog post / article on using Browserify with Backbone, Marionette, MongoDB, and Nodejs. If you want to get the real-deal scoop on how to set up this epic stack (which is the same stack I’m using in my projects, these days), check out this post! It’s got all the goods that you need.
Not Just Backbone, Though
At this point, Browserify is my module system of choice for the browser. I’ve done more with Browserify than just Backbone / Marionette, too. It works well for most of the libraries and frameworks I’ve tried it with, so far. Note that I have not tried it with Ember or Angular, though. You’ll have to ask someone else about those.
So, if you’re looking for a good module system for your browser, check out browserify. It’s been rocking my world for the last few weeks, and I’ve already got it in production in at least 3 pages on SignalLeaf.
But, How Does This Compare To ES6 Modules?
I have no idea. I haven’t had a chance to look at ES6 modules, yet. I hope to do that some time soon, though. As soon as I can get some experience, there, I’ll blog the comparison / difference.
Get Your Magic On
Ok, get to it. Go install browserify and build awesome modules, instead of frustrating boilerplate with a thousand plugins and shims!