Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you organize large JS/jQuery code bases across your entire website?

How do you organize large JS/jQuery codebases across your entire website? There are plenty of good resources on how to organize pieces of your code, but nothing really about how to pull it all together and fit each piece into place: side wide code organization, multiple pages using the same code, staying DRY with loose coupling, etc.

Below is how I deal with it. I've never been comfortable organizing my code like this, because I think it's sloppy and can lead to maintainability/scaling problems, but I don't really know any better.

I realize I everyone has their own set of requirements and there’s no turn-key solutions, but I’d love to hear some opinions about what I’m doing wrong, WHY I’m doing it wrong, and suggestions on how to write more maintainable code.

What I think I’m really trying to get at:

  1. How do you deal with logic that you need to use in multiple places, on multiple pages?

  2. How do you organize page-specific code? Is namespacing each page into a global object a good idea?1.

  3. What do you do from the start to ensure you’re not constantly re-writing your organization patterns as your app grows larger and larger? I’m probably on my 4th iteration writing this thing.2.

Each page receives the main application.js file. Each additional page has it's own application.pagename.js file. I use server side logic to include the files (first checking to see if one even exists for the page - some pages don't need JS), and then init them in order.

So my home page looks like:

<script src="js/application.js"></script>
<script src="js/application.index.js"></script>
<script>
    MyApp.init();
    MyApp.index.init();
</script>

my URL convention is /page/subpage/id/. I have about 10 pages and a whole slew of subpages, each subpage requiring their own logic. see the last example in this post.

Most of my code is already modularized into either jQuery UI widgets or jQuery plugins, so I'd say 75% of the code in these files is require()'ing a widget and initing it.

I use requireJS to pull in widgets as necessary.

// application.js
var MyApp = {
    init: function(){
        var self = this;

        // these widgets are available on every single page
        // notice the call to jquery.deparam.js - i'll use this later to init subpage logic.
        require(['js/widget1.js', 'js/widget2.js', 'js/widget3.js', 'js/jquery.deparam.js'], function(){

            // deparam the query string.  I'll use this later.
            self.querystring = $.deparam.querystring();

            // init widgets once the document is ready
            $(function(){
                $("#widget1").widget1();
                $("#widget2").widget2();

                // make these bindings available immediately as well.
                self.rebindable();
            });
        });
    },

    // I use jQuery UI Dialog extensively as a window manager, and each dialog is loaded
    // via an AJAX request.  I'll call this method after each AJAX request to
    // rebind some key widgets.
    rebindable: function(){
        $("#widget3").widget3();
    }
};

// application.index.js
// home page specific stuff.  this file is only included on the home page.
MyApp.index = {

    // my convention is that init is automatically called after the script
    // is included in a page, outside of a doc.ready statement.
    init: function(){
        var self = this;

        require(['js/widget4.js'], function(){
            $(function(){
                self.widget4( $("#foo") );
            });
        });
    },

    // passing elements to each method allows me to call this init code
    // outside of the index page.  I can require() this file, and only init
    // widget4, and even use a different element.
    widget4: function( element ){
        var config = {
            something: "custom to the home page"
        };

        element.widget4( config );
    }
};


// application.foo.js
// page "foo" stuff
MyApp.foo = {

    init: function(){
        var self = this;

        // this page happens to use the same widget3 and configuration present 
        // in MyApp.index.  this is where things can get sloppy and unmaintainable
        // really quickly.
        require(['js/application.index.js'], function(){
            $(function(){
                MyApp.index.widget3( $("#bar") );
            });
        });

        // page "foo" has three subpages (or actions) and require
        // their own logic.  url convention:  /foo/subpage1/
        // init whichever page we're on...
        switch( self.querystring.subpage ){
            case "subpage1":
                self.subpage1.init();
                break;
            case "subpage2":
                self.subpage2.init();
                break;
            case "subpage3":
                self.subpage3.init();
                break;
        }
    },

    subpage1: function(){
        init: function(){
            var self = this;

            // once the DOM is ready init dialog.
            $(function(){
                self.dialog( $("#openDialog") );
            });
        },

        dialog: function( element ){
            element.bind("click", function(){
                $('<div></div>').dialog({
                    open: function(){

                        // see what i'm doing here?
                        MyApp.rebindable();

                        // maybe more bindings specific to this
                        // dialog here
                    }
                });
            });
        }
    },

    subpage2: function(){
        init: function(){
        }
    },

    subpage3: function(){
        init: function(){
        }
    }
};
like image 279
ehynds Avatar asked Sep 08 '10 14:09

ehynds


People also ask

What is jQuery used for?

jQuery is a lightweight, "write less, do more", JavaScript library. The purpose of jQuery is to make it much easier to use JavaScript on your website. jQuery takes a lot of common tasks that require many lines of JavaScript code to accomplish, and wraps them into methods that you can call with a single line of code.


1 Answers

To help me answer your specific questions, please allow me talk a little about JavaScriptMVC's features:

Controller will improve your jQuery widgets, taking care of setup / teardown, extensibility.

View adds client side templates that can be built into your app.

Model abstracts the service / data layer minimizing and localizing JS changes if your server changes.

Steal does dependency management, compression, and code cleaning. It will even take all your scripts across all your pages, figure out shared dependencies, and combine scripts into an optimal payload.

FuncUnit makes testing your apps as easy as possible.

DocumentJS ... well ... documents your code

.

Now on your specific questions:

How to deal with logic used in multiple places?

I use StealJS's dependency management system to load the functionality I need into my page. Dependency management is absolutely necessary on apps of a certain size. RequireJS is a good choice if you are able to build it easily.

How do you organize page specific code

Page specific code should be as small as possible. It typically involves loading dependencies and a "MainController". That main controller configures the page to obey the functional / business requirements of that page. It's typically namespaced as something like:

App.Controllers.Main

how do you stop writing the same patterns

Well, I suggest using a framework that has stable patterns for development. Also, keep your modules / plugins / widgets as small (and testable) as possible. This will make these parts much less likely to change.

Finally ....

It seems your biggest struggle tension between:

  • shared functionality
  • multiple pages
  • timely load times

So picking a solid dependency management tool is super critical. StealJS could help you get very optimal loading times, but you'd have to stray from JavaScriptMVC's standard folder organization due to your larger number of pages.

RequireJS is more flexible, but you're going to have to load a lot of files. Not only will this be slow, this is going to start making you create lots of big JS files that aren't very organized.

If you are happy with the loading times and feel like they won't cause you to squeeze code into files it doesn't belong, your current solution seems like it will work.

I think the secret to maintainable development is how easy your system/framework allows you to isolate concerns. It's important to break up your app into the smallest parts possible. Plus, you should be testing these parts. People get side-tracked by thinking about their pages functionality. But to really scale development you really need something that allows you to break up your app into little parts, load those parts easily, and somehow get the app to still run fast in production.

like image 144
Justin Meyer Avatar answered Oct 01 '22 16:10

Justin Meyer